diff --git a/.cruft.json b/.cruft.json index 01a9e689..c79bc1fe 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/Ouranosinc/cookiecutter-pypackage", - "commit": "240a2c784f89cb1e2c5952869413c5582e590345", + "commit": "64eceda7d95aeb8937fa9961989d3d617a525c04", "checkout": null, "context": { "cookiecutter": { @@ -14,6 +14,7 @@ "version": "0.2.0", "use_pytest": "y", "use_black": "y", + "use_conda": "y", "add_pyup_badge": "n", "make_docs": "y", "command_line_interface": "Click", diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..1116575b --- /dev/null +++ b/.flake8 @@ -0,0 +1,30 @@ +[flake8] +exclude = + .eggs, + .git, + build, + docs, + tests +ignore = + AZ100, + AZ200, + AZ300, + C, + D, + E, + F, + W503 +per-file-ignores = +rst-roles = + doc, + mod, + py:attr, + py:attribute, + py:class, + py:const, + py:data, + py:func, + py:meth, + py:mod, + py:obj, + py:ref diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f57c2c83..dc5f20e3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,7 +5,7 @@ - This PR fixes #xyz - [ ] (If applicable) Documentation has been added / updated (for bug fixes / features). - [ ] (If applicable) Tests have been added. -- [ ] HISTORY.rst has been updated (with summary of main changes). +- [ ] CHANGES.rst has been updated (with summary of main changes). - [ ] Link to issue (:issue:`number`) and pull request (:pull:`number`) has been added. ### What kind of change does this PR introduce? diff --git a/.github/workflows/actions-versions-updater.yml b/.github/workflows/actions-versions-updater.yml new file mode 100644 index 00000000..bb05b24e --- /dev/null +++ b/.github/workflows/actions-versions-updater.yml @@ -0,0 +1,24 @@ +name: GitHub Actions Version Updater + +on: + schedule: + # 12:00 AM on the first of every month + - cron: '0 0 1 * *' + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + with: + # This requires a personal access token with the privileges to push directly to `main` + token: ${{ secrets.WORKFLOW_TOKEN }} + persist-credentials: true + - name: Run GitHub Actions Version Updater + uses: saadmk11/github-actions-version-updater@v0.8.1 + with: + token: ${{ secrets.WORKFLOW_TOKEN }} + committer_email: 'bumpversion[bot]@ouranos.ca' + committer_username: 'update-github-actions[bot]' + pull_request_title: '[bot] Update GitHub Action Versions' diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml new file mode 100644 index 00000000..ebc7727a --- /dev/null +++ b/.github/workflows/bump-version.yml @@ -0,0 +1,58 @@ +name: "Bump Patch Version" + +on: + push: + branches: + - main + paths-ignore: + - .cruft.json + - .editorconfig + - .github/**.yml + - .gitignore + - .pre-commit-config.yaml + - .yamllint.yaml + - .zenodo.json + - AUTHORS.rst + - CONTRIBUTING.rst + - CHANGES.rst + - Makefile + - .readthedocs.yml + - docs/*.py + - docs/*.rst + - environment-docs.yml + - environment-dev.yml + - setup.cfg + - pyproject.toml + - tests/**.py + - tox.ini + - figanos/__init__.py + workflow_dispatch: + +jobs: + bump_patch_version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Config Commit Bot + run: | + git config --local user.email "bumpversion[bot]@ouranos.ca" + git config --local user.name "bumpversion[bot]" + - name: Current Version + run: echo "current_version=$(grep -E '__version__' figanos/__init__.py | cut -d ' ' -f3)" + - name: Bump Patch Version + run: | + pip install bump-my-version + echo "Bumping version" + bump-my-version bump --tag patch + echo "new_version=$(grep -E '__version__' figanos/__init__.py | cut -d ' ' -f3)" + - name: Push Changes + uses: ad-m/github-push-action@master + with: + force: false + github_token: ${{ secrets.WORKFLOW_TOKEN }} + branch: ${{ github.ref }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0a528328..23aa8209 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,40 +5,41 @@ on: branches: - main paths-ignore: - - HISTORY.rst - - MANIFEST.in + - CHANGES.rst - README.rst - - setup.py + - pyproject.toml - setup.cfg - figanos/__init__.py pull_request: jobs: - black: - name: Black (Python${{ matrix.python-version }}) + lint: + name: Lint (Python${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: matrix: python-version: - - "3.8" + - "3.x" steps: - name: Cancel previous runs uses: styfle/cancel-workflow-action@0.11.0 with: access_token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install tox - run: pip install tox + run: | + python -m pip install tox - name: Run linting suite - run: tox -e black + run: | + python -m tox -e lint - test: - name: test-${{ matrix.tox-env }} (Python${{ matrix.python-version }}) - needs: black + test-pypi: + name: Test with Python${{ matrix.python-version }} (Python${{ matrix.python-version }} + tox) + needs: lint runs-on: ubuntu-latest strategy: matrix: @@ -52,38 +53,76 @@ jobs: - tox-env: "py311" python-version: "3.11" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} -# - name: Install tox -# run: pip install tox -# - name: Test with tox -# run: tox -e ${{ matrix.tox-env }} -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# COVERALLS_FLAG_NAME: run-${{ matrix.tox-env }} -# COVERALLS_PARALLEL: true -# COVERALLS_SERVICE_NAME: github - - name: Install figanos (no dependencies) + - name: Install GDAL + run: | + sudo apt-get update + sudo apt-get install -y libgdal-dev + - name: Install tox + run: pip install tox + - name: Test with tox + run: tox -e ${{ matrix.tox-env }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_FLAG_NAME: run-${{ matrix.tox-env }} + COVERALLS_PARALLEL: true + COVERALLS_SERVICE_NAME: github + + test-conda: + name: Test with Python${{ matrix.python-version }} (Anaconda) + needs: lint + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} + steps: + - uses: actions/checkout@v4 + - name: Setup Conda (Micromamba) with Python${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v1 + with: + cache-downloads: true + environment-file: environment-dev.yml + create-args: >- + mamba + python=3.11 + - name: Conda and Mamba versions + run: | + mamba --version + echo "micromamba $(micromamba --version)" + - name: Install figanos + run: | + python -m pip install --no-deps . + - name: Check versions + run: | + conda list + python -m pip check || true + - name: Test with pytest run: | - python -m pip install -e . --no-dependencies - - name: Smoke tests + python -m pytest --cov figanos + - name: Report coverage run: | - python -m pip install pytest - pytest + python -m coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_FLAG_NAME: run-py311-conda + COVERALLS_PARALLEL: true + COVERALLS_SERVICE_NAME: github -# finish: -# needs: -# - test -# runs-on: ubuntu-latest -# container: python:3-slim -# steps: -# - name: Coveralls Finished -# run: | -# pip install --upgrade coveralls -# coveralls --finish -# env: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -# COVERALLS_SERVICE_NAME: github + finish: + needs: + - test-pypi + - test-conda + runs-on: ubuntu-latest + container: python:3-slim + steps: + - name: Coveralls Finished + run: | + python -m pip install --upgrade coveralls + python -m coveralls --finish + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_SERVICE_NAME: github diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 08a5bf77..fd8758a5 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -8,22 +8,22 @@ on: jobs: build-n-publish-pypi: name: Build and publish Python 🐍 distributions 📦 to PyPI - environment: production runs-on: ubuntu-latest + environment: production permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python3 uses: actions/setup-python@v4 with: python-version: "3.x" - name: Install packaging libraries run: | - pip install setuptools wheel + python -m pip install flit - name: Build a binary wheel and a source tarball run: | - python setup.py sdist bdist_wheel + python -m flit build - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tag-testpypi.yml b/.github/workflows/tag-testpypi.yml index ef6c5894..be281ae7 100644 --- a/.github/workflows/tag-testpypi.yml +++ b/.github/workflows/tag-testpypi.yml @@ -3,28 +3,47 @@ name: Publish Python 🐍 distributions 📦 to TestPyPI on: push: tags: - - '*' + - 'v*.*' # Push events to matching v*, i.e. v1.0, v20.15.10 jobs: - build-n-publish-testpypi: + + release: + name: Create Release from tag + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '.0') + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Create Release + uses: softprops/action-gh-release@v1 + env: + # This token is provided by Actions, you do not need to create your own token + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + name: Release ${{ github.ref }} + draft: true + prerelease: false + + deploy-testpypi: name: Build and publish Python 🐍 distributions 📦 to TestPyPI - environment: staging runs-on: ubuntu-latest + environment: staging permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python3 uses: actions/setup-python@v4 with: python-version: "3.x" - name: Install packaging libraries run: | - pip install setuptools wheel + python -m pip install flit - name: Build a binary wheel and a source tarball run: | - python setup.py sdist bdist_wheel + python -m flit build - name: Publish distribution 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61605ddd..024c6781 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,15 +11,19 @@ repos: rev: v4.5.0 hooks: - id: trailing-whitespace - exclude: setup.cfg - id: end-of-file-fixer - id: debug-statements - id: check-json - id: pretty-format-json args: [ '--autofix', '--no-ensure-ascii', '--no-sort-keys' ] exclude: .ipynb + - id: check-toml - id: check-yaml args: [ '--allow-multiple-documents' ] + - repo: https://github.com/pappasam/toml-sort + rev: v0.23.1 + hooks: + - id: toml-sort-fix - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: @@ -29,23 +33,22 @@ repos: hooks: - id: black exclude: ^docs/ - - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 - hooks: - - id: flake8 - additional_dependencies: [ 'flake8-rst-docstrings' ] - args: ['--config=setup.cfg'] - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort args: [ '--profile=black', '--filter-files', "--add_imports='from __future__ import annotations'" ] exclude: ^docs/ - - repo: https://github.com/pycqa/pydocstyle - rev: 6.3.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.4 hooks: - - id: pydocstyle - args: [ '--convention=numpy', '--match="(?!test_).*\.py"' ] + - id: ruff + - repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + additional_dependencies: [ 'flake8-alphabetize', 'flake8-rst-docstrings' ] + args: [ '--config=.flake8' ] - repo: https://github.com/keewis/blackdoc rev: v0.3.9 hooks: @@ -57,21 +60,16 @@ repos: - id: nbstripout files: '.ipynb' args: [ '--extra-keys=metadata.kernelspec' ] - - repo: https://github.com/pycqa/pydocstyle - rev: 6.3.0 - hooks: - - id: pydocstyle - args: [ '--config=setup.cfg' ] - repo: https://github.com/adrienverge/yamllint.git rev: v1.33.0 hooks: - id: yamllint args: [ '--config-file=.yamllint.yaml' ] -# Git version too old on server -# - repo: https://github.com/mgedmin/check-manifest -# rev: "0.49" -# hooks: -# - id: check-manifest + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.27.1 + hooks: + - id: check-github-workflows + - id: check-readthedocs - repo: meta hooks: - id: check-hooks-apply diff --git a/.readthedocs.yml b/.readthedocs.yml index d812bec2..9738fc25 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -22,7 +22,7 @@ build: - sphinx-apidoc -o docs/apidoc --module-first figanos conda: - environment: environment.yml + environment: environment-dev.yml python: install: diff --git a/HISTORY.rst b/CHANGES.rst similarity index 78% rename from HISTORY.rst rename to CHANGES.rst index d114f606..b210bd1a 100644 --- a/HISTORY.rst +++ b/CHANGES.rst @@ -1,6 +1,6 @@ -======= -History -======= +========= +Changelog +========= 0.3.0 (Unreleased) ------------------ @@ -15,6 +15,7 @@ New features and enhancements * Logo plotting now supports both PNG and SVG file types (via `cairosvg`). (:pull:`119`). * Use small geojson in the notebook. (:pull:`124`). * Add the Colours of Figanos page (:issue:`126`, :pull:`127`). +* Figanos now adheres to PEPs 517/518/621 using the `flit` backend for building and packaging. (:pull:`135`). Bug fixes ^^^^^^^^^ @@ -30,10 +31,20 @@ Internal changes * Cleaned up the docstrings of a few functions, added some module-level strings, minor typo fixes. * Set `nbsphinx` in the documentation to always run (with th exception of one complex cell). * The `environment.yml` Python version is set to 3.11 to reduce the dependency solver complexity. +* The `cookiecutter` template has been updated to the latest commit via `cruft`. (:pull:`138`): + * `Manifest.in`, `requirements_dev.txt`, `requirements_docs.txt` and `setup.py` have been removed. + * `pyproject.toml` has been added, with most package configurations migrated into it. + * `HISTORY.rst` has been renamed to `CHANGES.rst`. + * `actions-version-updater.yml` has been added to automate the versioning of the package. + * `bump-version.yml` has been added to automate patch versioning of the package. + * `pre-commit` hooks have been updated to the latest versions; `check-toml` and `toml-sort` have been added to cleanup the `pyproject.toml` file. + * `ruff` has been added to the linting tools to replace most `flake8` and `pydocstyle` verifications. + * GitHub workflows now run proper pytest suites for `conda`-based testing. Bug fixes ^^^^^^^^^ * Fixed an issue with the `divergent` argument getting ignored (:pull:`132`). +* Some small documentation fixes for working uniquely in a `conda` environment. (:pull:`138`). 0.2.0 (2023-06-19) ------------------ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 4df44c47..0af200a2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -4,8 +4,7 @@ Contributing ============ -Contributions are welcome, and they are greatly appreciated! Every little bit -helps, and credit will always be given. +Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways: @@ -26,21 +25,17 @@ If you are reporting a bug, please include: Fix Bugs ~~~~~~~~ -Look through the GitHub issues for bugs. Anything tagged with "bug" and "help -wanted" is open to whoever wants to implement it. +Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ -Look through the GitHub issues for features. Anything tagged with "enhancement" -and "help wanted" is open to whoever wants to implement it. +Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ -figanos could always use more documentation, whether as part of the -official figanos docs, in docstrings, or even on the web in blog posts, -articles, and such. +``figanos`` could always use more documentation, whether as part of the official ``figanos`` docs, in docstrings, or even on the web in blog posts, articles, and such. Submit Feedback ~~~~~~~~~~~~~~~ @@ -55,113 +50,172 @@ If you are proposing a feature: are welcome :) Get Started! -------------------------------------------------- +------------ -Ready to contribute? Here's how to set up `figanos` for local development for developers outside Ouranos. +.. note:: -1. Fork the `figanos` repo on GitHub. -2. Clone your fork locally:: + If you are new to using GitHub and `git`, please read `this guide `_ first. + +.. warning:: + + Anaconda Python users: Due to the complexity of some packages, the default dependency solver can take a long time to resolve the environment. Consider running the following commands in order to speed up the process:: + + $ conda install -n base conda-libmamba-solver + $ conda config --set solver libmamba + + For more information, please see the following link: https://www.anaconda.com/blog/a-faster-conda-for-a-growing-community + + Alternatively, you can use the `mamba `_ package manager, which is a drop-in replacement for ``conda``. If you are already using `mamba`, replace the following commands with ``mamba`` instead of ``conda``. + +Ready to contribute? Here's how to set up ``figanos`` for local development for developers outside Ouranos. + +#. Fork the ``figanos`` repo on GitHub. +#. Clone your fork locally:: $ git clone git@github.com:your_name_here/figanos.git -3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: +#. Install your local copy into a development environment. You can create a new Anaconda development environment with:: + + $ conda env create -f environment-dev.yml + $ conda activate figanos + $ flit install --symlink - $ mkvirtualenv figanos - $ cd figanos/ - $ pip install -e . + This installs ``figanos`` in an "editable" state, meaning that changes to the code are immediately seen by the environment. -4. To ensure a consistent style, please install the pre-commit hooks to your repo:: +#. To ensure a consistent coding style, install the ``pre-commit`` hooks to your local clone:: $ pre-commit install - Special style and formatting checks will be run when you commit your changes. You - can always run the hooks on their own with: + On commit, ``pre-commit`` will check that ``black``, ``blackdoc``, ``isort``, ``flake8``, and ``ruff`` checks are passing, perform automatic fixes if possible, and warn of violations that require intervention. If your commit fails the checks initially, simply fix the errors, re-add the files, and re-commit. + + You can also run the hooks manually with:: $ pre-commit run -a -5. Create a branch for local development:: + If you want to skip the ``pre-commit`` hooks temporarily, you can pass the ``--no-verify`` flag to `$ git commit`. + +#. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. -6. When you're done making changes, check that your changes pass flake8, black, and the - tests, including testing other Python versions with tox:: +#. When you're done making changes, we **strongly** suggest running the tests in your environment or with the help of ``tox``:: - $ flake8 figanos tests - $ black --check figanos tests - $ python setup.py test or pytest + $ python -m pytest + # Or, to run multiple build tests $ tox - To get flake8, black, and tox, just pip install them into your virtualenv. - -6. Commit your changes and push your branch to GitHub:: +#. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature -7. If you are editing the docs, compile and open them with:: + If ``pre-commit`` hooks fail, try re-committing your changes (or, if need be, you can skip them with `$ git commit --no-verify`). +#. Submit a `Pull Request `_ through the GitHub website. + +#. When pushing your changes to your branch on GitHub, the documentation will automatically be tested to reflect the changes in your Pull Request. This build process can take several minutes at times. If you are actively making changes that affect the documentation and wish to save time, you can compile and test your changes beforehand locally with:: + + # To generate the html and open it in your browser $ make docs - # or to simply generate the html - $ cd docs/ - $ make html + # To only generate the html + $ make autodoc + $ make -C docs html + # To simply test that the docs pass build checks + $ tox -e docs + +#. Once your Pull Request has been accepted and merged to the ``main`` branch, several automated workflows will be triggered: -7. Submit a pull request through the GitHub website. + - The ``bump-version.yml`` workflow will automatically bump the patch version when pull requests are pushed to the ``main`` branch on GitHub. **It is not recommended to manually bump the version in your branch when merging (non-release) pull requests (this will cause the version to be bumped twice).** + - `ReadTheDocs` will automatically build the documentation and publish it to the `latest` branch of `figanos` documentation website. + - If your branch is not a fork (ie: you are a maintainer), your branch will be automatically deleted. + +You will have contributed your first changes to ``figanos``! Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: -1. The pull request should include tests. -2. If the pull request adds functionality, the docs should be updated. Put - your new functionality into a function with a docstring, and add the - feature to the list in README.rst. -3. The pull request should work for all major supported Python versions (3.8, 3.9, 3.10, and 3.11). +#. The pull request should include tests and should aim to provide `code coverage `_ for all new lines of code. You can use the ``--cov-report html --cov figanos`` flags during the call to ``pytest`` to generate an HTML report and analyse the current test coverage. + +#. If the pull request adds functionality, the docs should also be updated. Put your new functionality into a function with a docstring, and add the feature to the list in ``README.rst``. + +#. The pull request should work for Python 3.8, 3.9, 3.10, and 3.11. Check that the tests pass for all supported Python versions. + Tips ---- To run a subset of tests:: -$ pytest tests.test_figanos + $ pytest tests.test_figanos +To run specific code style checks:: + + $ black --check figanos tests + $ isort --check figanos tests + $ blackdoc --check figanos docs + $ ruff figanos tests + $ flake8 figanos tests + +To get ``black``, ``isort ``blackdoc``, ``ruff``, and ``flake8`` (with plugins ``flake8-alphabetize`` and ``flake8-rst-docstrings``) simply install them with `pip` (or `conda`) into your environment. Versioning/Tagging ------------------ -A reminder for the maintainers on how to deploy. -Make sure all your changes are committed (including an entry in HISTORY.rst). -Then run:: +A reminder for the **maintainers** on how to deploy. This section is only relevant when producing a new point release for the package. + +.. warning:: + + It is important to be aware that any changes to files found within the ``figanos`` folder (with the exception of ``figanos/__init__.py``) will trigger the ``bump-version.yml`` workflow. Be careful not to commit changes to files in this folder when preparing a new release. + +#. Create a new branch from `main` (e.g. `release-0.2.0`). +#. Update the `CHANGES.rst` file to change the `Unreleased` section to the current date. +#. Bump the version in your branch to the next version (e.g. `v0.1.0 -> v0.2.0`):: + + .. code-block:: shell + + $ bump-my-version bump minor # In most cases, we will be releasing a minor version + $ git push + +#. Create a pull request from your branch to `main`. +#. Once the pull request is merged, create a new release on GitHub. On the main branch, run: + + .. code-block:: shell -$ bumpversion patch # possible: major / minor / patch -$ git push -$ git push --tags + $ git tag v0.2.0 + $ git push --tags + + This will trigger a GitHub workflow to build the package and upload it to TestPyPI. At the same time, the GitHub workflow will create a draft release on GitHub. Assuming that the workflow passes, the final release can then be published on GitHub by finalizing the draft release. + +#. Once the release is published, the `publish-pypi.yml` workflow will go into an `awaiting approval` mode on Github Actions. Only authorized users may approve this workflow (notifications will be sent) to trigger the upload to PyPI. + +.. warning:: + + Uploads to PyPI can **never** be overwritten. If you make a mistake, you will need to bump the version and re-release the package. If the package uploaded to PyPI is broken, you should modify the GitHub release to mark the package as broken, as well as yank the package (mark the version "broken") on PyPI. Packaging --------- -When a new version has been minted (features have been successfully integrated test coverage and stability is adequate), -maintainers should update the pip-installable package (wheel and source release) on PyPI as well as the binary on conda-forge. +When a new version has been minted (features have been successfully integrated test coverage and stability is adequate), maintainers should update the pip-installable package (wheel and source release) on PyPI as well as the binary on conda-forge. The simple approach ~~~~~~~~~~~~~~~~~~~ -The simplest approach to packaging for general support (pip wheels) requires the following packages installed: - * build - * setuptools - * twine - * wheel +The simplest approach to packaging for general support (pip wheels) requires that ``flit`` be installed:: + + $ python -m pip install flit From the command line on your Linux distribution, simply run the following from the clone's main dev branch:: # To build the packages (sources and wheel) - $ python -m build --sdist --wheel + $ python -m flit build # To upload to PyPI - $ twine upload dist/* + $ python -m flit publish dist/* The new version based off of the version checked out will now be available via `pip` (`$ pip install figanos`). @@ -171,19 +225,25 @@ Releasing on conda-forge Initial Release ^^^^^^^^^^^^^^^ -In order to prepare an initial release on conda-forge, we *strongly* suggest consulting the following links: +Before preparing an initial release on conda-forge, we *strongly* suggest consulting the following links: * https://conda-forge.org/docs/maintainer/adding_pkgs.html * https://github.com/conda-forge/staged-recipes +In order to create a new conda build recipe, to be used when proposing packages to the conda-forge repository, we strongly suggest using the ``grayskull`` tool:: + + $ python -m pip install grayskull + $ grayskull pypi figanos + +For more information on ``grayskull``, please see the following link: https://github.com/conda/grayskull + Before updating the main conda-forge recipe, we echo the conda-forge documentation and *strongly* suggest performing the following checks: * Ensure that dependencies and dependency versions correspond with those of the tagged version, with open or pinned versions for the `host` requirements. - * If possible, configure tests within the conda-forge build CI (e.g. `imports: figanos`, `commands: pytest figanos`) + * If possible, configure tests within the conda-forge build CI (e.g. `imports: figanos`, `commands: pytest figanos`). Subsequent releases ^^^^^^^^^^^^^^^^^^^ -If the conda-forge feedstock recipe is built from PyPI, then when a new release is published on PyPI, `regro-cf-autotick-bot` will open Pull Requests automatically on the conda-forge feedstock. -It is up to the conda-forge feedstock maintainers to verify that the package is building properly before merging the Pull Request to the main branch. +If the conda-forge feedstock recipe is built from PyPI, then when a new release is published on PyPI, `regro-cf-autotick-bot` will open Pull Requests automatically on the conda-forge feedstock. It is up to the conda-forge feedstock maintainers to verify that the package is building properly before merging the Pull Request to the main branch. Building sources for wide support with `manylinux` image ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -192,8 +252,7 @@ Building sources for wide support with `manylinux` image This section is for building source files that link to or provide links to C/C++ dependencies. It is not necessary to perform the following when building pure Python packages. -In order to do ensure best compatibility across architectures, we suggest building wheels using the `PyPA`'s `manylinux` -docker images (at time of writing, we endorse using `manylinux_2_24_x86_64`). +In order to do ensure best compatibility across architectures, we suggest building wheels using the `PyPA`'s `manylinux` docker images (at time of writing, we endorse using `manylinux_2_24_x86_64`). With `docker` installed and running, begin by pulling the image:: @@ -203,11 +262,10 @@ From the figanos source folder we can enter into the docker container, providing $ sudo docker run --rm -ti -v $(pwd):/figanos -w /figanos quay.io/pypa/manylinux_2_24_x86_64 bash -Finally, to build the wheel, we run it against the provided Python3.8 binary:: +Finally, to build the wheel, we run it against the provided Python3.9 binary:: - $ /opt/python/cp38-cp38m/bin/python setup.py sdist bdist_wheel + $ /opt/python/cp39-cp39m/bin/python -m build --sdist --wheel -This will then place two files in `figanos/dist/` ("figanos-1.2.3-py3-none-any.whl" and "figanos-1.2.3.tar.gz"). -We can now leave our docker container (`$ exit`) and continue with uploading the files to PyPI:: +This will then place two files in `figanos/dist/` ("figanos-1.2.3-py3-none-any.whl" and "figanos-1.2.3.tar.gz"). We can now leave our docker container (`$ exit`) and continue with uploading the files to PyPI:: $ twine upload dist/* diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 0bfe6d38..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,26 +0,0 @@ -include AUTHORS.rst -include CONTRIBUTING.rst -include HISTORY.rst -include LICENSE -include README.rst -include .zenodo.json -include requirements_dev.txt -include requirements_docs.txt - -recursive-include figanos *.json *.mplstyle *.py *.txt -recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif -recursive-include figanos/data *.json *.png *.txt *.yml - -recursive-exclude * __pycache__ -recursive-exclude * *.py[co] - -exclude .coveralls.yml -exclude .cruft.json -exclude .editorconfig -exclude .pre-commit-config.yaml -exclude .readthedocs.yml -exclude .yamllint.yaml -exclude Makefile -exclude environment.yml -exclude environment-docs.yml -exclude tox.ini diff --git a/Makefile b/Makefile index b77edbc1..756536b3 100644 --- a/Makefile +++ b/Makefile @@ -53,15 +53,18 @@ clean-test: ## remove test and coverage artifacts rm -fr .pytest_cache lint/flake8: ## check style with flake8 - flake8 figanos tests + ruff figanos tests + flake8 --config=.flake8 figanos tests lint/black: ## check style with black black --check figanos tests + blackdoc --check figanos docs + isort --check figanos tests lint: lint/flake8 lint/black ## check style test: ## run tests quickly with the default Python - pytest + python -m pytest test-all: ## run tests on every Python version with tox tox @@ -87,13 +90,15 @@ endif servedocs: docs ## compile the docs watching for changes watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . -release: dist ## package and upload a release - twine upload dist/* - dist: clean ## builds source and wheel package - python -m build --sdist - python -m build --wheel + python -m flit build ls -l dist +release: dist ## package and upload a release + python -m flit publish dist/* + install: clean ## install the package to the active Python's site-packages - python setup.py install + python -m flit install + +dev: clean ## install the package to the active Python's site-packages + python -m flit install --symlink diff --git a/README.rst b/README.rst index f18e7e1a..e3a8ac8a 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,6 @@ figanos ======= - .. image:: https://img.shields.io/pypi/v/figanos.svg :target: https://pypi.python.org/pypi/figanos :alt: PyPI @@ -21,15 +20,11 @@ figanos Figanos: Tool to create FIGures in the OurANOS style - -Pour nous partager vos codes à ajouter dans figanos, s.v.p créer un issue sur le repo github avec une description de la fonction et -le code de celle-ci. - +Pour nous partager vos codes à ajouter dans figanos, s.v.p créer un issue sur le repo github avec une description de la fonction et le code de celle-ci. * Free software: Apache Software License 2.0 * Documentation: https://figanos.readthedocs.io. - Features -------- diff --git a/docs/changes.rst b/docs/changes.rst new file mode 100644 index 00000000..d9e113ec --- /dev/null +++ b/docs/changes.rst @@ -0,0 +1 @@ +.. include:: ../CHANGES.rst diff --git a/docs/history.rst b/docs/history.rst deleted file mode 100644 index 25064996..00000000 --- a/docs/history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../HISTORY.rst diff --git a/docs/index.rst b/docs/index.rst index 8c7a259b..db5c3dd3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,7 @@ Need help? api contributing authors - history + changes .. toctree:: :maxdepth: 1 diff --git a/docs/installation.rst b/docs/installation.rst index dde5c32d..8de7b42e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -10,7 +10,7 @@ To install figanos, run these commands in your terminal: .. code-block:: console $ mamba install -c conda-forge gdal - $ pip install figanos + $ python -m pip install figanos Development Installation (conda + pip) -------------------------------------- @@ -28,12 +28,14 @@ Then you can create the environment and install the package: .. code-block:: console $ cd figanos - $ conda env create -f environment.yml + $ conda env create -f environment-dev.yml -Finally, perform an `--editable` install of figanos: +Finally, perform an `--symlink` install of figanos: .. code-block:: console - $ pip install -e . + $ flit install --symlink + # Or + $ make dev .. _Github repo: https://github.com/Ouranosinc/figanos diff --git a/environment.yml b/environment-dev.yml similarity index 81% rename from environment.yml rename to environment-dev.yml index 397f2d0d..efeb4b5f 100644 --- a/environment.yml +++ b/environment-dev.yml @@ -27,10 +27,11 @@ dependencies: - holoviews # Development - black - - bump2version - - coverage - - coveralls + - bump-my-version >=0.12.0 + - coverage >=6.2,<7.0 + - coveralls >=3.3.1 - flake8 + - flake8-rst-docstrings - isort - pip - pre-commit @@ -45,10 +46,9 @@ dependencies: - nbsphinx - notebook - pandoc - - sphinx + - sphinx >=6.2.0 - sphinx-autoapi - - sphinx-book-theme - - sphinx-click + - sphinx-book-theme >=1.0.0 - sphinx-codeautolink - sphinx-copybutton - sphinxcontrib-napoleon diff --git a/figanos/__init__.py b/figanos/__init__.py index 6b4ed4f3..693ae7d6 100644 --- a/figanos/__init__.py +++ b/figanos/__init__.py @@ -1,4 +1,4 @@ -"""Top-level package for figanos.""" +"""Outils pour produire des graphiques informatifs sur les impacts des changements climatiques.""" __author__ = """Sarah-Claude Bourdeau-Goulet""" __email__ = "bourdeau-goulet.sarah-claude@ouranos.ca" diff --git a/figanos/_logo.py b/figanos/_logo.py index 9c356774..9b8c9773 100644 --- a/figanos/_logo.py +++ b/figanos/_logo.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import logging import shutil import urllib.parse import urllib.request import warnings from pathlib import Path -from typing import Optional, Union import platformdirs import yaml @@ -56,10 +57,10 @@ class Logos: def __init__(self) -> None: """Initialize the Logo class instance.""" - self._config = None - self._catalogue = None - self._default = None - self._logos = {} + self._config: Path = Path(platformdirs.user_config_dir("figanos")) / "logos" + self._catalogue: Path = self._config / LOGO_CONFIG_FILE + self._default: Path = self._config / "figanos_logo.png" + self._logos: dict[str, str] = {} self.reload_config() if not self._logos.get("default"): @@ -67,30 +68,18 @@ def __init__(self) -> None: self.set_logo(_figanos_logo) self.set_logo(_figanos_logo, name="default") - @property - def config(self) -> Path: - """The path to the logo configuration folder.""" - if self._config is None: - self._config = ( - Path(platformdirs.user_config_dir("figanos", ensure_exists=True)) - / "logos" - ) - return self._config - @property def catalogue(self) -> Path: """The path to the logo configuration file.""" - if self._catalogue is None: - self._catalogue = self.config / LOGO_CONFIG_FILE return self._catalogue @property - def default(self) -> str: + def default(self) -> Path: """The path to the default logo.""" return self._default @default.setter - def default(self, value: Union[str, Path]): + def default(self, value: Path): """Set a default logo.""" self._default = value @@ -103,7 +92,7 @@ def _setup(self) -> None: warnings.warn( f"No logo configuration file found. Creating one at {self.catalogue}." ) - self.config.mkdir(parents=True, exist_ok=True) + self._config.mkdir(parents=True, exist_ok=True) with open(self.catalogue, "w") as f: yaml.dump(dict(logos={}), f) @@ -115,13 +104,13 @@ def __repr__(self) -> str: """Return the default logo filepath.""" return f"{self._default}" - def __getitem__(self, args) -> Optional[str]: + def __getitem__(self, args) -> str | None: """Retrieve a logo filepath by its name. If it does not exist, it will be installed, with the filepath returned. """ try: - return self._logos[args] + return str(self._logos[args]) except (KeyError, TypeError): if isinstance(args, tuple): return self.set_logo(*args) @@ -141,9 +130,7 @@ def installed(self) -> list: """Retrieve a list of installed logos.""" return list(self._logos.keys()) - def set_logo( - self, path: Union[str, Path], name: Optional[str] = None - ) -> Optional[str]: + def set_logo(self, path: str | Path, name: str | None = None) -> str | None: """Copy an image at a given path to the config folder and map it to a given name in the catalogue.""" _logo_mapping = yaml.safe_load(self.catalogue.read_text())["logos"] @@ -151,7 +138,7 @@ def set_logo( if logo_path.exists() and logo_path.is_file(): if name is None: name = logo_path.stem.replace("-", "_") - install_logo_path = self.config / logo_path.name + install_logo_path = self._config / logo_path.name if not install_logo_path.exists(): shutil.copy(logo_path, install_logo_path) @@ -163,12 +150,13 @@ def set_logo( if name != "default": return self._logos[name] else: - return self._default + return str(self._default) elif not logo_path.exists(): warnings.warn(f"Logo file `{logo_path}` not found. Not setting logo.") elif not logo_path.is_file(): warnings.warn(f"Logo path `{logo_path}` is a folder. Not setting logo.") + return None def install_ouranos_logos(self, *, permitted: bool = False) -> None: """Fetch and install the Ouranos logo. @@ -184,11 +172,11 @@ def install_ouranos_logos(self, *, permitted: bool = False) -> None: for orientation in ["horizontal", "vertical"]: for colour in ["couleur", "blanc", "noir"]: file = f"logo-ouranos-{orientation}-{colour}.svg" - if not (self.config / file).exists(): + if not (self._config / file).exists(): logo_url = urllib.parse.urljoin(OURANOS_LOGOS_URL, file) try: - urllib.request.urlretrieve(logo_url, self.config / file) - self.set_logo(self.config / file) + urllib.request.urlretrieve(logo_url, self._config / file) + self.set_logo(self._config / file) except Exception as e: logging.error( f"Error downloading or setting Ouranos logo: {e}" @@ -196,12 +184,12 @@ def install_ouranos_logos(self, *, permitted: bool = False) -> None: if Path(self.default).stem == "figanos_logo": _default_ouranos_logo = ( - self.config / "logo-ouranos-horizontal-couleur.svg" + self._config / "logo-ouranos-horizontal-couleur.svg" ) warnings.warn(f"Setting default logo to {_default_ouranos_logo}.") self.set_logo(_default_ouranos_logo, name="default") self.reload_config() - print(f"Ouranos logos installed at: {self.config}.") + print(f"Ouranos logos installed at: {self._config}.") else: warnings.warn( "You have not indicated that you have permission to use the Ouranos logo. " diff --git a/figanos/matplotlib/utils.py b/figanos/matplotlib/utils.py index 34a83df7..1b5508a6 100644 --- a/figanos/matplotlib/utils.py +++ b/figanos/matplotlib/utils.py @@ -75,10 +75,10 @@ def get_localized_term(term, locale=None): return TERMS[term][locale] -def empty_dict(param): +def empty_dict(param) -> dict: """Return empty dict if input is None.""" if param is None: - param = {} + param = dict() return param @@ -175,7 +175,7 @@ def get_array_categ(array: xr.DataArray | xr.Dataset) -> str: def get_attributes( - string: str, xr_obj: xr.DataArray | xr.Dataset, locale: str = None + string: str, xr_obj: xr.DataArray | xr.Dataset, locale: str | None = None ) -> str: """Fetch attributes or dims corresponding to keys from Xarray objects. @@ -360,8 +360,8 @@ def sort_lines(array_dict: dict[str, Any]) -> dict[str, str]: def loc_mpl( - loc: str | tuple[float, float] | int, -) -> tuple[tuple[float, float], tuple[float, float], str, str]: + loc: str | tuple[int | float, int | float] | int, +) -> tuple[tuple[float, float], tuple[int | float, int | float], str, str]: """Find coordinates and alignment associated to loc string. Parameters @@ -574,11 +574,11 @@ def load_image( The scaled image. """ if pathlib.Path(im).suffix == ".png": - im = mpl.pyplot.imread(im) - original_height, original_width = im.shape[:2] + image = mpl.pyplot.imread(im) + original_height, original_width = image.shape[:2] if height is None and width is None: - return im + return image warnings.warn( "The scikit-image library is used to resize PNG images. This may affect logo image quality." @@ -596,7 +596,7 @@ def load_image( # Only height is provided, derive zoom factor for width based on aspect ratio width = (height / original_height) * original_width - return resize(im, (height, width, im.shape[2]), anti_aliasing=True) + return resize(image, (height, width, image.shape[2]), anti_aliasing=True) elif pathlib.Path(im).suffix == ".svg": cairo_kwargs = dict(url=im) @@ -779,7 +779,7 @@ def fill_between_label( def get_var_group( path_to_json: str | pathlib.Path, da: xr.DataArray | None = None, - unique_str: str = None, + unique_str: str | None = None, ) -> str: """Get IPCC variable group from DataArray or a string using a json file (figanos/data/ipcc_colors/variable_groups.json). @@ -1047,7 +1047,7 @@ def get_mpl_styles() -> dict[str, str]: folder = pathlib.Path(__file__).parent / "style/" paths = sorted(folder.glob("*.mplstyle")) names = [str(p).split("/")[-1].removesuffix(".mplstyle") for p in paths] - styles = {name: path for name, path in zip(names, paths)} + styles = {str(name): path for name, path in zip(names, paths)} return styles diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..8b0da955 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,259 @@ +[build-system] +requires = ["flit_core >=3.8,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "figanos" +authors = [ + {name = "Sarah-Claude Bourdeau-Goulet", email = "bourdeau-goulet.sarah-claude@ouranos.ca"}, + {name = "Juliette Lavoie", email = "lavoie.juliette@ouranos.ca"}, + {name = "Alexis Beaupré-Laperrière", email = "Beaupre-Laperriere.Alexis@ouranos.ca"} +] +maintainers = [ + {name = "Sarah-Claude Bourdeau-Goulet", email = "bourdeau-goulet.sarah-claude@ouranos.ca"}, + {name = "Juliette Lavoie", email = "lavoie.juliette@ouranos.ca"}, + {name = "Trevor James Smith", email = "smith.trevorj@ouranos.ca"} +] +readme = {file = "README.rst", content-type = "text/x-rst"} +requires-python = ">=3.8.0" +keywords = ["figanos"] +license = {file = "LICENSE"} +classifiers = [ + "Development Status :: 2 - Pre-Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython" +] +dynamic = ["description", "version"] +dependencies = [ + "cartopy", + "cairosvg", + "geopandas", + "matplotlib", + "numpy", + "pandas", + "platformdirs", + "pyyaml", + "seaborn", + "scikit-image", + "xarray", + "xclim>=0.38" +] + +[project.optional-dependencies] +dev = [ + # Dev tools and testing + "pip>=23.3", + "bump-my-version>=0.12.0", + "watchdog>=3.0.0", + "flake8>=6.1.0", + "flake8-alphabetize>=0.0.21", + "flake8-rst-docstrings>=0.3.0", + "flit", + "tox>=4.5.1", + "coverage>=6.2.2,<7.0.0", + "coveralls>=3.3.1", + "pytest>=7.3.1", + "pytest-cov>=4.0.0", + "black>=23.11.0", + "blackdoc>=0.3.9", + "isort>=5.12.0", + "pre-commit>=3.3.2" +] +docs = [ + # Documentation and examples + "dask", + "h5py", + "netcdf4", + "zarr", + "geoviews", + "holoviews", + "ipykernel", + "ipython", + "jupyter_client", + "nbsphinx", + "sphinx-codeautolink", + "sphinx-copybutton", + "sphinx>=6.2.0", + "sphinx-book-theme>=1.0", + "sphinxcontrib-napoleon" +] + +[project.urls] +"Homepage" = "https://figanos.readthedocs.io/" +"Changelog" = "https://figanos.readthedocs.io/en/stable/changes.html" +"Issue tracker" = "https://github.com/Ouranosinc/figanos/issues" +"Source" = "https://github.com/Ouranosinc/figanos" +"About Ouranos" = "https://www.ouranos.ca/en/" + +[tool] + +[tool.black] +target-version = [ + "py38", + "py39", + "py310", + "py311" +] + +[tool.bumpversion] +current_version = "0.2.0" +commit = true +tag = false +tag_name = "{new_version}" +allow_dirty = false +serialize = ["{major}.{minor}.{patch}"] +parse = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)" + +[[tool.bumpversion.files]] +filename = "figanos/__init__.py" +search = "__version__ = \"{current_version}\"" +replace = "__version__ = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = "tests/test_figanos.py" +search = "__version__ = \"{current_version}\"" +replace = "__version__ = \"{new_version}\"" + +[[tool.bumpversion.files]] +filename = ".cruft.json" +search = "\"version\": \"{current_version}\"" +replace = "\"version\": \"{new_version}\"" + +[tool.coverage.run] +relative_files = true +include = ["figanos/*"] +omit = ["tests/*.py"] + +[tool.flit.sdist] +include = [ + ".zenodo.json", + "AUTHORS.rst", + "CHANGES.rst", + "CONTRIBUTING.rst", + "LICENSE", + "Makefile", + "README.rst", + "docs/*.rst", + "docs/Makefile", + "docs/_static/_images/*.gif", + "docs/_static/_images/*.jpg", + "docs/_static/_images/*.png", + "docs/_static/_images/*.rst", + "docs/conf.py", + "docs/make.bat", + "docs/notebooks/.ipynb", + "environment-dev.yml", + "tests/*.py", + "figanos", + "tox.ini" +] +exclude = [ + "*.py[co]", + "__pycache__", + ".coveralls.yml", + ".editorconfig", + ".flake8", + ".gitignore", + ".pre-commit-config.yaml", + ".readthedocs.yml", + ".yamllint.yaml", + "docs/_*", + "docs/apidoc/modules.rst", + "docs/apidoc/figanos*.rst" +] + +[tool.isort] +profile = "black" +py_version = 38 + +[tool.mypy] +python_version = 3.8 +show_error_codes = true +warn_return_any = true +warn_unused_configs = true + +[[tool.mypy.overrides]] +module = [ + "cairosvg.*", + "cartopy.*", + "matplotlib.*", + "mpl_toolkits.*", + "geopandas.*", + "holoviews.*", + "numpy.*", + "pandas.*", + "scipy.*", + "seaborn.*", + "skimage.transform.*", + "xarray.*", + "xclim.*", + "yaml.*" +] +ignore_missing_imports = true + +[tool.pytest.ini_options] +addopts = [ + "--verbose", + "--color=yes" +] +filterwarnings = ["ignore::UserWarning"] +testpaths = "tests" + +[tool.ruff] +src = [""] +line-length = 150 +target-version = "py38" +exclude = [ + ".eggs", + ".git", + "build", + "docs", + "tests" +] +ignore = [ + "D205", + "D400", + "D401" +] +select = [ + "C9", + "D", + "E", + "F", + "W" +] + +[tool.ruff.flake8-bandit] +check-typed-exception = true + +[tool.ruff.format] +line-ending = "auto" + +[tool.ruff.isort] +known-first-party = ["figanos"] +case-sensitive = true +detect-same-package = false +lines-after-imports = 1 +no-lines-before = ["future", "standard-library"] + +[tool.ruff.mccabe] +max-complexity = 15 + +[tool.ruff.per-file-ignores] +"figanos/**/__init__.py" = ["F401", "F403"] +"figanos/matplotlib/**/*.py" = ["C901"] + +[tool.ruff.pycodestyle] +max-doc-length = 180 + +[tool.ruff.pydocstyle] +convention = "numpy" diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index bccebb04..00000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,15 +0,0 @@ -pip==23.3 -bump2version==1.0.1 -wheel==0.40.0 -build==0.10.0 -flake8==6.0.0 -tox==4.5.1 -coverage==7.2.5 -coveralls==3.3.1 -sphinx==7.0.1 -twine==4.0.2 -pytest==7.3.1 -pytest-cov==4.0.0 -black==23.3.0 -isort==5.12.0 -pre-commit==3.3.2 diff --git a/requirements_docs.txt b/requirements_docs.txt deleted file mode 100644 index 7e222ffc..00000000 --- a/requirements_docs.txt +++ /dev/null @@ -1,20 +0,0 @@ -xclim>=0.38 -dask -h5py -netcdf4 -pyyaml -zarr -geoviews -holoviews -ipykernel -ipython -jupyter_client -nbsphinx -pandoc -sphinx -sphinx-autoapi -sphinx-click -sphinx-codeautolink -sphinx-copybutton -sphinx_book_theme -sphinxcontrib-napoleon diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 9c112791..00000000 --- a/setup.cfg +++ /dev/null @@ -1,77 +0,0 @@ -[bumpversion] -current_version = 0.2.0 -commit = True -tag = False - -[bumpversion:file:setup.py] -search = version="{current_version}" -replace = version="{new_version}" - -[bumpversion:file:figanos/__init__.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" - -[bumpversion:file:tests/test_figanos.py] -search = __version__ = "{current_version}" -replace = __version__ = "{new_version}" - -[bumpversion:file:.cruft.json] -search = "version": "{current_version}", -replace = "version": "{new_version}", - -[aliases] -test = pytest - -[tool:pytest] -collect_ignore = ['setup.py'] -addopts = - --verbose -filterwarnings = - ignore::UserWarning - -[flake8] -exclude = - .git, - docs, - build, - .eggs, - docs/conf.py, -max-line-length = 88 -max-complexity = 12 -ignore = - C901 - E203 - E231 - E266 - E501 - F401 - F403 - W503 - W504 -per-file-ignores = - tests/*:E402 -rst-roles = - mod, - py:attr, - py:attribute, - py:class, - py:const, - py:data, - py:func, - py:meth, - py:mod, - py:obj, - py:ref, - ref - -[coverage:run] -relative_files = True -omit = */tests/*.py - -[isort] -profile = black -py_version = 38 - -[pydocstyle] -convention = numpy -match = ((?!test_|conf).)*\.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 3866360a..00000000 --- a/setup.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python - -"""The setup script.""" - -from setuptools import find_packages, setup - -with open("README.rst") as readme_file: - readme = readme_file.read() - -requirements = [ - "cartopy", - "cairosvg", - "geopandas", - "matplotlib", - "numpy", - "pandas", - "platformdirs", - "pyyaml", - "seaborn", - "scikit-image", - "xarray", - "xclim", -] - -docs_requirements = [ - dependency for dependency in open("requirements_docs.txt").readlines() -] - -dev_requirements = [ - dependency for dependency in open("requirements_dev.txt").readlines() -] - -setup( - author="Sarah-Claude Bourdeau-Goulet", - author_email="bourdeau-goulet.sarah-claude@ouranos.ca", - python_requires=">=3.8", - classifiers=[ - "Development Status :: 2 - Pre-Alpha", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Natural Language :: English", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - ], - description="Outils pour produire des graphiques informatifs sur les impacts des changements climatiques.", - install_requires=requirements, - license="Apache Software License 2.0", - long_description=readme, - long_description_content_type="text/x-rst", - include_package_data=True, - keywords="figanos", - name="figanos", - packages=find_packages(include=["figanos", "figanos.*"]), - test_suite="tests", - extras_require={ - "docs": docs_requirements, - "dev": dev_requirements, - }, - project_urls={ - "Source": "https://github.com/Ouranosinc/figanos", - "Issue tracker": "https://github.com/Ouranosinc/figanos/issues", - "About Ouranos": "https://www.ouranos.ca/en/", - }, - version="0.2.0", - zip_safe=False, -) diff --git a/tests/test_figanos.py b/tests/test_figanos.py index 5c533b93..05ecf941 100644 --- a/tests/test_figanos.py +++ b/tests/test_figanos.py @@ -27,7 +27,7 @@ def test_content(response): def test_package_metadata(): """Test the package metadata.""" - project = pkgutil.get_loader("figanos").get_filename() + project = pkgutil.get_loader("figanos").get_filename() # noqa metadata = pathlib.Path(project).resolve().parent.joinpath("__init__.py") diff --git a/tox.ini b/tox.ini index bd282306..0a6a3374 100644 --- a/tox.ini +++ b/tox.ini @@ -1,20 +1,24 @@ [tox] min_version = 4.0 envlist = - black + lint py{38,39,310,311} docs coveralls requires = - pip >= 21.0 + flit + pip >= 23.3.0 opts = --verbose -[testenv:black] +[testenv:lint] skip_install = True deps = - flake8 black + blackdoc + isort + flake8 + ruff commands = make lint allowlist_externals = @@ -33,6 +37,8 @@ setenv = PYTEST_ADDOPTS = "--color=yes" PYTHONPATH = {toxinidir} passenv = + CI + COVERALLS_* GITHUB_* extras = dev