diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..9320c99 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,18 @@ +{ + "template": "https://github.com/ecmwf-projects/cookiecutter-conda-package.git", + "commit": "a5d6e8a6678af8f61a69af97a78852d0c595fedc", + "checkout": null, + "context": { + "cookiecutter": { + "project_name": "elevation", + "project_slug": "elevation", + "project_short_description": "Python script to download global terrain digital elevation models, SRTM 30m DEM and SRTM 90m DEM.", + "copyright_holder": "B-Open Solutions srl", + "copyright_year": "2016", + "mypy_strict": "False", + "integration_tests": "True", + "_template": "https://github.com/ecmwf-projects/cookiecutter-conda-package.git" + } + }, + "directory": null +} diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml index f7598d0..995919e 100644 --- a/.github/workflows/on-push.yml +++ b/.github/workflows/on-push.yml @@ -1,104 +1,196 @@ name: on-push -on: [push] +on: + push: + branches: + - main + tags: + - '*' + pull_request: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash -l {0} jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.0 + + combine-environments: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Install conda-merge + run: | + $CONDA/bin/python -m pip install conda-merge + - name: Combine environments + run: | + for SUFFIX in ci integration; do + $CONDA/bin/conda-merge ci/environment-$SUFFIX.yml environment.yml > ci/combined-environment-$SUFFIX.yml || exit + done + - name: Archive combined environments + uses: actions/upload-artifact@v3 + with: + name: combined-environments + path: ci/combined-environment-*.yml + unit-tests: - runs-on: ${{ matrix.os }}-latest - strategy: - max-parallel: 5 - matrix: - os: [ubuntu] - python: [3.6, 3.7, 3.8, 3.9] - extras: [''] - include: - - os: macos - python: 3.8 - - os: ubuntu - python: 3.8 - - os: ubuntu - python: 3.8 - extras: -minimal - - os: windows - python: 3.8 + name: unit-tests (3.10) + needs: combine-environments + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 + - uses: actions/checkout@v3 + - name: Download combined environments + uses: actions/download-artifact@v3 + with: + name: combined-environments + path: ci + - name: Install Conda environment with Micromamba + uses: mamba-org/provision-with-micromamba@v15 with: - python-version: ${{ matrix.python }} - activate-environment: ${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }} - environment-file: tests/environment-${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }}.yml - - name: Test with pytest - shell: bash -l {0} + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + channels: conda-forge + cache-env: true + extra-specs: | + python=3.10 + - name: Install package run: | - conda install pytest pytest-cov pytest-mock - pip install --no-deps -e . - pytest -v --cov=. --cov-report=xml . - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 + python -m pip install --no-deps -e . + - name: Run tests + run: | + make unit-tests COV_REPORT=xml - docs: + type-check: + needs: [combine-environments, unit-tests] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 + - uses: actions/checkout@v3 + - name: Download combined environments + uses: actions/download-artifact@v3 + with: + name: combined-environments + path: ci + - name: Install Conda environment with Micromamba + uses: mamba-org/provision-with-micromamba@v15 with: - python-version: 3.8 - activate-environment: ubuntu-3.8 - environment-file: tests/environment-ubuntu-3.8.yml - - name: Build documentation with Sphinx - shell: bash -l {0} + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + channels: conda-forge + cache-env: true + extra-specs: | + python=3.10 + - name: Install package run: | - conda install sphinx sphinx_rtd_theme - pip install --no-deps -e . - python setup.py build_sphinx - - name: Test README with pytest - shell: bash -l {0} + python -m pip install --no-deps -e . + - name: Run code quality checks run: | - conda install pytest pytest-cov - pytest -v --cov=. --cov-report=xml --cov-branch README.rst + make type-check - code-quality: + docs-build: + needs: [combine-environments, unit-tests] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Lint with flake8 + - uses: actions/checkout@v3 + - name: Download combined environments + uses: actions/download-artifact@v3 + with: + name: combined-environments + path: ci + - name: Install Conda environment with Micromamba + uses: mamba-org/provision-with-micromamba@v15 + with: + environment-file: ci/combined-environment-ci.yml + environment-name: DEVELOP + channels: conda-forge + cache-env: true + extra-specs: | + python=3.10 + - name: Install package run: | - $CONDA/bin/conda install flake8 - # stop the build if there are Python syntax errors or undefined names - $CONDA/bin/flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - $CONDA/bin/flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + python -m pip install --no-deps -e . + - name: Build documentation + run: | + make docs-build - code-style: + integration-tests: + needs: [combine-environments, unit-tests] + if: | + success() && true runs-on: ubuntu-latest + strategy: + matrix: + include: + - python-version: '3.10' + extra: -integration + steps: - - uses: actions/checkout@v2 - - name: Check code style with black + - uses: actions/checkout@v3 + - name: Download combined environments + uses: actions/download-artifact@v3 + with: + name: combined-environments + path: ci + - name: Install Conda environment with Micromamba + uses: mamba-org/provision-with-micromamba@v15 + with: + environment-file: ci/combined-environment${{ matrix.extra }}.yml + environment-name: DEVELOP${{ matrix.extra }} + channels: conda-forge + cache-env: true + extra-specs: | + python=${{ matrix.python-version }} + - name: Install package run: | - $CONDA/bin/conda install black - $CONDA/bin/black --check . - - name: Check code style with isort + python -m pip install --no-deps -e . + - name: Run tests run: | - $CONDA/bin/conda install isort - $CONDA/bin/isort . + make unit-tests COV_REPORT=xml distribution: runs-on: ubuntu-latest + needs: [unit-tests, type-check, docs-build, integration-tests] + if: | + always() && + needs.unit-tests.result == 'success' && + needs.type-check.result == 'success' && + needs.docs-build.result == 'success' && + (needs.integration-tests.result == 'success' || needs.integration-tests.result == 'skipped') steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - name: Install packages + run: | + $CONDA/bin/python -m pip install build twine - name: Build distributions run: | - $CONDA/bin/conda install pip setuptools wheel - $CONDA/bin/python setup.py sdist bdist_wheel + $CONDA/bin/python -m build + - name: Check wheels + run: | + cd dist || exit + $CONDA/bin/python -m pip install elevation*.whl || exit + $CONDA/bin/python -m twine check * || exit + $CONDA/bin/python -c "import elevation" - name: Publish a Python distribution to PyPI if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ - password: ${{ secrets.pypi_password }} + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/weekly.yml b/.github/workflows/weekly.yml deleted file mode 100644 index c7b1d41..0000000 --- a/.github/workflows/weekly.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: weekly - -on: - schedule: - - cron: '0 9 * * 1' - workflow_dispatch: - -jobs: - environment: - runs-on: ${{ matrix.os }}-latest - strategy: - max-parallel: 5 - fail-fast: false - matrix: - os: [ubuntu] - python: [3.6, 3.7, 3.8, 3.9] - extras: [''] - include: - - os: macos - python: 3.8 - - os: windows - python: 3.8 - - os: ubuntu - python: 3.8 - extras: -minimal - - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - auto-update-conda: true - python-version: ${{ matrix.python }} - activate-environment: ${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }} - environment-file: environment${{ matrix.extras }}.in.yml - - name: Export concrete dependencies - shell: bash -l {0} - run: | - conda env export --no-build -f tests/environment-${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }}.yml - git diff - - name: Archive environment-${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }}.yml - uses: actions/upload-artifact@v2 - with: - name: environment-${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }}.yml - path: tests/environment-${{ matrix.os }}-${{ matrix.python }}${{ matrix.extras }}.yml diff --git a/.gitignore b/.gitignore index 7b55d6e..2684639 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,140 @@ +# setuptools-scm +version.py + +# Sphinx automatic generation of API +docs/_api/ + +# Created by https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks,vim,visualstudiocode,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=python,jupyternotebooks,vim,visualstudiocode,pycharm + +### JupyterNotebooks ### +# gitignore template for Jupyter Notebooks +# website: http://jupyter.org/ + +.ipynb_checkpoints +*/.ipynb_checkpoints/* + +# IPython +profile_default/ +ipython_config.py + +# Remove previous ipynb_checkpoints +# git rm -r .ipynb_checkpoints/ + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -8,7 +145,6 @@ __pycache__/ # Distribution / packaging .Python -env/ build/ develop-eggs/ dist/ @@ -20,10 +156,12 @@ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg -docs/html +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -38,13 +176,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml -*,cover +*.cover +*.py,cover .hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo @@ -52,15 +194,157 @@ coverage.xml # Django stuff: *.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy # Sphinx documentation docs/_build/ # PyBuilder +.pybuilder/ target/ -#Ipython Notebook -.ipynb_checkpoints +# Jupyter Notebook + +# IPython + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ # PyCharm -.idea \ No newline at end of file +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VisualStudioCode ### +.vscode/ +# .vscode/* +# !.vscode/settings.json +# !.vscode/tasks.json +# !.vscode/launch.json +# !.vscode/extensions.json +# !.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Ignore code-workspaces +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/python,jupyternotebooks,vim,visualstudiocode,pycharm diff --git a/.pre-commit-config-cruft.yaml b/.pre-commit-config-cruft.yaml new file mode 100644 index 0000000..b55f24f --- /dev/null +++ b/.pre-commit-config-cruft.yaml @@ -0,0 +1,7 @@ +repos: +- repo: https://github.com/cruft/cruft + rev: 2.15.0 + hooks: + - id: cruft + entry: cruft update -y + additional_dependencies: [toml] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3dd16ed --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-yaml + - id: check-toml + - id: check-added-large-files + - id: debug-statements + - id: mixed-line-ending +- repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black +- repo: https://github.com/keewis/blackdoc + rev: v0.3.8 + hooks: + - id: blackdoc + additional_dependencies: [black==22.3.0] +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.267 + hooks: + - id: ruff + args: [--fix] +- repo: https://github.com/executablebooks/mdformat + rev: 0.7.16 + hooks: + - id: mdformat +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.9.0 + hooks: + - id: pretty-format-yaml + args: [--autofix, --preserve-quotes] + - id: pretty-format-toml + args: [--autofix] diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9630f7e..ba1d387 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -33,4 +33,3 @@ in order to update the pinned version to the latest version run:: $ pip-compile -U --no-index requirements-tests.in $ pip-compile -U --no-index requirements-docs.in - diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9590ffa --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +PROJECT := elevation +CONDA := conda +CONDAFLAGS := +COV_REPORT := html + +default: qa unit-tests type-check + +qa: + pre-commit run --all-files + +unit-tests: + python -m pytest -vv --cov=. --cov-report=$(COV_REPORT) --doctest-glob="*.md" --doctest-glob="*.rst" + +type-check: + python -m mypy . + +conda-env-update: + $(CONDA) env update $(CONDAFLAGS) -f ci/environment-ci.yml + $(CONDA) env update $(CONDAFLAGS) -f environment.yml + +docker-build: + docker build -t $(PROJECT) . + +docker-run: + docker run --rm -ti -v $(PWD):/srv $(PROJECT) + +template-update: + pre-commit run --all-files cruft -c .pre-commit-config-cruft.yaml + +docs-build: + cd docs && rm -fr _api && make clean && make html + +# DO NOT EDIT ABOVE THIS LINE, ADD COMMANDS BELOW diff --git a/README.rst b/README.rst index 30743a2..682d0e9 100644 --- a/README.rst +++ b/README.rst @@ -129,7 +129,7 @@ Every command has a corresponding API function in the ``elevation`` module: >>> import elevation >>> # clip the SRTM1 30m DEM of Rome and save it to Rome-DEM.tif ->>> elevation.clip(bounds=(12.35, 41.8, 12.65, 42), output='Rome-DEM.tif') +>>> elevation.clip(bounds=(12.35, 41.8, 12.65, 42), output="Rome-DEM.tif") >>> # clean up stale temporary files and fix the cache in the event of a server error >>> elevation.clean() @@ -168,4 +168,3 @@ License Elevation is free and open source software distributed under the terms of the `Apache License, Version 2.0 `_. - diff --git a/ci/environment-ci.yml b/ci/environment-ci.yml new file mode 100644 index 0000000..2ed2f19 --- /dev/null +++ b/ci/environment-ci.yml @@ -0,0 +1,18 @@ +# environment-ci.yml: Additional dependencies to install in the CI environment. +channels: +- conda-forge +- nodefaults +dependencies: +- make +- mypy +- myst-parser +- pre-commit +- pydata-sphinx-theme +- pytest +- pytest-cov +- sphinx +- sphinx-autoapi +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW +- pytest-mock +- pip: + - types-appdirs diff --git a/ci/environment-integration.yml b/ci/environment-integration.yml new file mode 100644 index 0000000..6a8511d --- /dev/null +++ b/ci/environment-integration.yml @@ -0,0 +1,9 @@ +# environment-integration.yml: Additional dependencies to install in the integration environment (e.g., pinned dependencies). +channels: +- conda-forge +- nodefaults +dependencies: +- make +- pytest +- pytest-cov +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py index 64f76f9..a0c89c2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,158 +1,76 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- +# Configuration file for the Sphinx documentation builder. # -# elevation documentation build configuration file -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html -# import sys -# import os -import pkg_resources -import sphinx.environment -import sphinx_rtd_theme -from docutils.utils import get_source_line +# -- Import and path setup --------------------------------------------------- -# "monkey patch" sphinx to omit any warnings of 'nonlocal image URI found'. -# Now we can `sphinx-build -W` to turn "warnings to errors" in test builds. +import os +import sys +import elevation -def _warn_node(self, msg, node, **kwargs): - if not msg.startswith('nonlocal image URI found:'): - self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs) +sys.path.insert(0, os.path.abspath("../")) +# -- Project information ----------------------------------------------------- -sphinx.environment.BuildEnvironment.warn_node = _warn_node +project = "elevation" +copyright = "2016, B-Open Solution srl" +author = "B-Open Solution srl" +version = elevation.__version__ +release = elevation.__version__ -# -- General configuration ------------------------------------------------ +# -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.ifconfig', - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.coverage', - 'sphinx.ext.imgmath', - 'sphinx.ext.viewcode', + "autoapi.extension", + "myst_parser", + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", ] -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'elevation' -copyright = u'2016-2021 B-Open Solutions srl' -author = u'B-Open Solutions srl' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The full version, including alpha/beta/rc tags. -release = pkg_resources.get_distribution("elevation").version -# The short X.Y version. -version = '.'.join(release.split('.')[:2]) +# autodoc configuration +autodoc_typehints = "none" + +# autoapi configuration +autoapi_dirs = ["../elevation"] +autoapi_ignore = ["*/version.py"] +autoapi_options = [ + "members", + "inherited-members", + "undoc-members", + "show-inheritance", + "show-module-summary", + "imported-members", +] +autoapi_root = "_api" -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None +# napoleon configuration +napoleon_google_docstring = False +napoleon_numpy_docstring = True +napoleon_preprocess_types = True -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -today_fmt = '%Y-%m-%d' +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False -# -- Options for HTML output ---------------------------------------------- +# -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = 'Elevation global DEM manager' - -# A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = 'elevation' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'elevationdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. - #'preamble': '', - # Latex figure (float) alignment - #'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'elevation.tex', 'elevation Documentation', 'B-Open Solutions srl', 'manual'), -] - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, 'elevation', 'elevation Documentation', [author], 1)] - - -# -- Options for Texinfo output ------------------------------------------- +# +html_theme = "pydata_sphinx_theme" -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - 'elevation', - 'elevation Documentation', - author, - 'elevation', - 'One line description of project.', - 'Miscellaneous', - ), -] +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/elevation/__init__.py b/elevation/__init__.py index 9ad0ede..fa16ecc 100644 --- a/elevation/__init__.py +++ b/elevation/__init__.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016-2021 B-Open Solutions srl - http://bopen.eu +# Copyright 2016, B-Open Solution srl. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,5 +12,40 @@ # See the License for the specific language governing permissions and # limitations under the License. +try: + # NOTE: the `version.py` file must not be present in the git repository + # as it is generated by setuptools at install time + from .version import __version__ +except ImportError: # pragma: no cover + # Local copy or not installed with setuptools + __version__ = "999" + # import all API functions and constants -from .datasource import * # pragma: no flakes +from .datasource import ( + CACHE_DIR, + DEFAULT_OUTPUT, + DEFAULT_PRODUCT, + MARGIN, + PRODUCTS, + TOOLS, + clean, + clip, + distclean, + info, + seed, +) + +__all__ = [ + "clip", + "seed", + "__version__", + "PRODUCTS", + "DEFAULT_PRODUCT", + "CACHE_DIR", + "DEFAULT_OUTPUT", + "MARGIN", + "info", + "clean", + "distclean", + "TOOLS", +] diff --git a/elevation/cli.py b/elevation/cli.py index 284c741..a2cfa6b 100644 --- a/elevation/cli.py +++ b/elevation/cli.py @@ -26,20 +26,20 @@ click.disable_unicode_literals_warning = True -CONTEXT_SETTINGS = dict(auto_envvar_prefix='EIO') +CONTEXT_SETTINGS = dict(auto_envvar_prefix="EIO") @click.group(context_settings=CONTEXT_SETTINGS) @click.version_option() @click.option( - '--product', + "--product", type=click.Choice(elevation.PRODUCTS), default=elevation.DEFAULT_PRODUCT, show_default=True, help="DEM product choice.", ) @click.option( - '--cache_dir', + "--cache_dir", type=click.Path(resolve_path=True, file_okay=False), default=elevation.CACHE_DIR, show_default=True, @@ -72,7 +72,9 @@ def info(**kwargs): @eio.command(short_help="Seed the DEM to given bounds.") -@click.option('--bounds', nargs=4, type=float, help="Output bounds: left bottom right top.") +@click.option( + "--bounds", nargs=4, type=float, help="Output bounds: left bottom right top." +) @click_merge_parent_params def seed(**kwargs): elevation.seed(**kwargs) @@ -80,23 +82,30 @@ def seed(**kwargs): @eio.command(short_help="Clip the DEM to given bounds.") @click.option( - '-o', - '--output', + "-o", + "--output", type=click.Path(resolve_path=True, dir_okay=False), default=elevation.DEFAULT_OUTPUT, show_default=True, help="Path to output file. Existing files will be overwritten.", ) -@click.option('--bounds', type=float, nargs=4, help="Output bounds in 'left bottom right top' order.") @click.option( - '-m', - '--margin', + "--bounds", + type=float, + nargs=4, + help="Output bounds in 'left bottom right top' order.", +) +@click.option( + "-m", + "--margin", default=elevation.MARGIN, show_default=True, help="Decimal degree margin added to the bounds. Use '%' for percent margin.", ) @click.option( - '-r', '--reference', help="Use the extent of a reference GDAL/OGR data source as output bounds." + "-r", + "--reference", + help="Use the extent of a reference GDAL/OGR data source as output bounds.", ) @click_merge_parent_params def clip(bounds, reference, **kwargs): diff --git a/elevation/datasource.py b/elevation/datasource.py index 66bf903..3fb9b67 100644 --- a/elevation/datasource.py +++ b/elevation/datasource.py @@ -26,22 +26,22 @@ # declare public all API functions and constants __all__ = [ - 'info', - 'seed', - 'clip', - 'clean', - 'distclean', - 'CACHE_DIR', - 'DEFAULT_PRODUCT', - 'PRODUCTS', - 'DEFAULT_OUTPUT', - 'MARGIN', - 'TOOLS', + "info", + "seed", + "clip", + "clean", + "distclean", + "CACHE_DIR", + "DEFAULT_PRODUCT", + "PRODUCTS", + "DEFAULT_OUTPUT", + "MARGIN", + "TOOLS", ] -CACHE_DIR = appdirs.user_cache_dir('elevation', 'bopen') -DEFAULT_OUTPUT = 'out.tif' -MARGIN = '0' +CACHE_DIR = appdirs.user_cache_dir("elevation", "bopen") +DEFAULT_OUTPUT = "out.tif" +MARGIN = "0" def srtm1_tile_ilonlat(lon, lat): @@ -53,7 +53,9 @@ def srtm3_tile_ilonlat(lon, lat): return (ilon + 180) // 5 + 1, (64 - ilat) // 5 -def srtm1_tiles_names(left, bottom, right, top, tile_name_template='{slat}/{slat}{slon}.tif'): +def srtm1_tiles_names( + left, bottom, right, top, tile_name_template="{slat}/{slat}{slon}.tif" +): ileft, itop = srtm1_tile_ilonlat(left, top) iright, ibottom = srtm1_tile_ilonlat(right, bottom) # special case often used *integer* top and right to avoid downloading unneeded tiles @@ -62,13 +64,15 @@ def srtm1_tiles_names(left, bottom, right, top, tile_name_template='{slat}/{slat if isinstance(right, int) or right.is_integer(): iright -= 1 for ilon in range(ileft, iright + 1): - slon = '%s%03d' % ('E' if ilon >= 0 else 'W', abs(ilon)) + slon = "%s%03d" % ("E" if ilon >= 0 else "W", abs(ilon)) for ilat in range(ibottom, itop + 1): - slat = '%s%02d' % ('N' if ilat >= 0 else 'S', abs(ilat)) + slat = "%s%02d" % ("N" if ilat >= 0 else "S", abs(ilat)) yield tile_name_template.format(**locals()) -def srtm3_tiles_names(left, bottom, right, top, tile_template='srtm_{ilon:02d}_{ilat:02d}.tif'): +def srtm3_tiles_names( + left, bottom, right, top, tile_template="srtm_{ilon:02d}_{ilat:02d}.tif" +): ileft, itop = srtm3_tile_ilonlat(left, top) iright, ibottom = srtm3_tile_ilonlat(right, bottom) for ilon in range(ileft, iright + 1): @@ -77,16 +81,18 @@ def srtm3_tiles_names(left, bottom, right, top, tile_template='srtm_{ilon:02d}_{ yield tile_template.format(**locals()) -def srtm_ellip_tiles_names(left, bottom, right, top, tile_name_template='{slat}{slon}_wgs84.tif'): +def srtm_ellip_tiles_names( + left, bottom, right, top, tile_name_template="{slat}{slon}_wgs84.tif" +): ileft, itop = srtm1_tile_ilonlat(left, top) iright, ibottom = srtm1_tile_ilonlat(right, bottom) for ilon in range(ileft, iright + 1): - slon = '%s%03d' % ('E' if ilon >= 0 else 'W', abs(ilon)) + slon = "%s%03d" % ("E" if ilon >= 0 else "W", abs(ilon)) for ilat in range(ibottom, itop + 1): - slat = '%s%02d' % ('N' if ilat >= 0 else 'S', abs(ilat)) - subdir = 'North' if ilat >= 0 else 'South' - north_subdir = 'North_30_60' if ilat >= 30 else 'North_0_29' + slat = "%s%02d" % ("N" if ilat >= 0 else "S", abs(ilat)) + subdir = "North" if ilat >= 0 else "South" + north_subdir = "North_30_60" if ilat >= 30 else "North_0_29" fname = tile_name_template.format(**locals()) if ilat >= 0: @@ -95,58 +101,64 @@ def srtm_ellip_tiles_names(left, bottom, right, top, tile_name_template='{slat}{ yield ("{subdir}/{fname}".format(**locals())) -DATASOURCE_MAKEFILE = pkgutil.get_data('elevation', 'datasource.mk').decode('utf-8') +DATASOURCE_MAKEFILE = pkgutil.get_data("elevation", "datasource.mk").decode("utf-8") SRTM1_ELLIP_SPEC = { - 'folders': ('spool', 'cache'), - 'file_templates': {'Makefile': DATASOURCE_MAKEFILE}, - 'datasource_url': 'https://opentopography.s3.sdsc.edu/raster/SRTM_GL1_Ellip/SRTM_GL1_Ellip_srtm', - 'tile_ext': '.tif', - 'compressed_pre_ext': '', - 'compressed_ext': '', - 'tile_names': srtm_ellip_tiles_names, + "folders": ("spool", "cache"), + "file_templates": {"Makefile": DATASOURCE_MAKEFILE}, + "datasource_url": "https://opentopography.s3.sdsc.edu/raster/SRTM_GL1_Ellip/SRTM_GL1_Ellip_srtm", + "tile_ext": ".tif", + "compressed_pre_ext": "", + "compressed_ext": "", + "tile_names": srtm_ellip_tiles_names, } SRTM1_SPEC = { - 'folders': ('spool', 'cache'), - 'file_templates': {'Makefile': DATASOURCE_MAKEFILE}, - 'datasource_url': 'https://s3.amazonaws.com/elevation-tiles-prod/skadi', - 'tile_ext': '.hgt', - 'compressed_pre_ext': '.hgt', - 'compressed_ext': '.hgt.gz', - 'tile_names': srtm1_tiles_names, + "folders": ("spool", "cache"), + "file_templates": {"Makefile": DATASOURCE_MAKEFILE}, + "datasource_url": "https://s3.amazonaws.com/elevation-tiles-prod/skadi", + "tile_ext": ".hgt", + "compressed_pre_ext": ".hgt", + "compressed_ext": ".hgt.gz", + "tile_names": srtm1_tiles_names, } SRTM3_SPEC = { - 'folders': ('spool', 'cache'), - 'file_templates': {'Makefile': DATASOURCE_MAKEFILE}, - 'datasource_url': 'https://srtm.csi.cgiar.org/wp-content/uploads/files/srtm_5x5/TIFF', - 'tile_ext': '.tif', - 'compressed_pre_ext': '', - 'compressed_ext': '.zip', - 'tile_names': srtm3_tiles_names, + "folders": ("spool", "cache"), + "file_templates": {"Makefile": DATASOURCE_MAKEFILE}, + "datasource_url": "https://srtm.csi.cgiar.org/wp-content/uploads/files/srtm_5x5/TIFF", + "tile_ext": ".tif", + "compressed_pre_ext": "", + "compressed_ext": ".zip", + "tile_names": srtm3_tiles_names, } PRODUCTS_SPECS = collections.OrderedDict( - [('SRTM1', SRTM1_SPEC), ('SRTM3', SRTM3_SPEC), ('SRTM1_ELLIP', SRTM1_ELLIP_SPEC),] + [ + ("SRTM1", SRTM1_SPEC), + ("SRTM3", SRTM3_SPEC), + ("SRTM1_ELLIP", SRTM1_ELLIP_SPEC), + ] ) PRODUCTS = list(PRODUCTS_SPECS) DEFAULT_PRODUCT = PRODUCTS[0] TOOLS = [ - ('GNU Make', 'make --version'), - ('curl', 'curl --help'), - ('unzip', 'unzip -v'), - ('gunzip', 'gunzip --version'), - ('gdal_translate', 'gdal_translate --version'), - ('gdalbuildvrt', 'gdalbuildvrt --version'), + ("GNU Make", "make --version"), + ("curl", "curl --help"), + ("unzip", "unzip -v"), + ("gunzip", "gunzip --version"), + ("gdal_translate", "gdal_translate --version"), + ("gdalbuildvrt", "gdalbuildvrt --version"), ] def ensure_tiles(path, ensure_tiles_names=(), **kwargs): - ensure_tiles = ' '.join(ensure_tiles_names) - variables_items = [('ensure_tiles', ensure_tiles)] - return util.check_call_make(path, targets=['download'], variables=variables_items, **kwargs) + ensure_tiles = " ".join(ensure_tiles_names) + variables_items = [("ensure_tiles", ensure_tiles)] + return util.check_call_make( + path, targets=["download"], variables=variables_items, **kwargs + ) # FIXME: force=True is an emergency hack to ensure that the file always contains the intended body @@ -160,14 +172,20 @@ def ensure_setup(cache_dir, product, force=True): def do_clip(path, bounds, output, product=DEFAULT_OUTPUT, **kwargs): run_id = uuid.uuid4().hex with util.lock_vrt(path, product): - util.check_call_make(path, targets=['copy_vrt'], variables=[('run_id', run_id)]) + util.check_call_make(path, targets=["copy_vrt"], variables=[("run_id", run_id)]) left, bottom, right, top = bounds - projwin = '%s %s %s %s' % (left, top, right, bottom) - variables_items = [('output', output), ('projwin', projwin), ('run_id', run_id)] - return util.check_call_make(path, targets=['clip'], variables=variables_items) - - -def seed(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT, bounds=None, max_download_tiles=9, **kwargs): + projwin = "%s %s %s %s" % (left, top, right, bottom) + variables_items = [("output", output), ("projwin", projwin), ("run_id", run_id)] + return util.check_call_make(path, targets=["clip"], variables=variables_items) + + +def seed( + cache_dir=CACHE_DIR, + product=DEFAULT_PRODUCT, + bounds=None, + max_download_tiles=9, + **kwargs +): """Seed the DEM to given bounds. :param cache_dir: Root of the DEM cache folder. @@ -177,7 +195,7 @@ def seed(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT, bounds=None, max_download :param kwargs: Pass additional kwargs to ensure_tiles. """ datasource_root, spec = ensure_setup(cache_dir, product) - ensure_tiles_names = list(spec['tile_names'](*bounds)) + ensure_tiles_names = list(spec["tile_names"](*bounds)) # FIXME: emergency hack to enforce the no-bulk-download policy if len(ensure_tiles_names) > max_download_tiles: raise RuntimeError( @@ -189,19 +207,24 @@ def seed(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT, bounds=None, max_download ensure_tiles(datasource_root, ensure_tiles_names, **kwargs) with util.lock_vrt(datasource_root, product): - util.check_call_make(datasource_root, targets=['all']) + util.check_call_make(datasource_root, targets=["all"]) return datasource_root def build_bounds(bounds, margin=MARGIN): left, bottom, right, top = bounds - if margin.endswith('%'): + if margin.endswith("%"): margin_percent = float(margin[:-1]) margin_lon = (right - left) * margin_percent / 100 margin_lat = (top - bottom) * margin_percent / 100 else: margin_lon = margin_lat = float(margin) - return (left - margin_lon, bottom - margin_lat, right + margin_lon, top + margin_lat) + return ( + left - margin_lon, + bottom - margin_lat, + right + margin_lon, + top + margin_lat, + ) def clip(bounds, output=DEFAULT_OUTPUT, margin=MARGIN, **kwargs): @@ -225,7 +248,7 @@ def info(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT): :param product: DEM product choice. """ datasource_root, _ = ensure_setup(cache_dir, product) - util.check_call_make(datasource_root, targets=['info']) + util.check_call_make(datasource_root, targets=["info"]) def clean(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT): @@ -235,7 +258,7 @@ def clean(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT): :param product: DEM product choice. """ datasource_root, _ = ensure_setup(cache_dir, product) - util.check_call_make(datasource_root, targets=['clean']) + util.check_call_make(datasource_root, targets=["clean"]) def distclean(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT): @@ -245,4 +268,4 @@ def distclean(cache_dir=CACHE_DIR, product=DEFAULT_PRODUCT): :param product: DEM product choice. """ datasource_root, _ = ensure_setup(cache_dir, product) - util.check_call_make(datasource_root, targets=['distclean']) + util.check_call_make(datasource_root, targets=["distclean"]) diff --git a/elevation/util.py b/elevation/util.py index 4cd74ec..e0454ad 100644 --- a/elevation/util.py +++ b/elevation/util.py @@ -21,7 +21,7 @@ import fasteners -FOLDER_LOCKFILE_NAME = '.folder_lock' +FOLDER_LOCKFILE_NAME = ".folder_lock" def selfcheck(tools): @@ -34,15 +34,15 @@ def selfcheck(tools): try: subprocess.check_output(check_cli, shell=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError: - msg.append('%r not found or not usable.' % tool_name) - return '\n'.join(msg) if msg else 'Your system is ready.' + msg.append("%r not found or not usable." % tool_name) + return "\n".join(msg) if msg else "Your system is ready." @contextmanager def lock_tiles(datasource_root, tile_names): locks = [] for tile_name in tile_names: - lockfile_name = os.path.join(datasource_root, 'cache', tile_name + '.lock') + lockfile_name = os.path.join(datasource_root, "cache", tile_name + ".lock") locks.append(fasteners.InterProcessLock(lockfile_name)) for lock in locks: @@ -56,7 +56,9 @@ def lock_tiles(datasource_root, tile_names): @contextmanager def lock_vrt(datasource_root, product): - with fasteners.InterProcessLock(os.path.join(datasource_root, product + '.vrt.lock')): + with fasteners.InterProcessLock( + os.path.join(datasource_root, product + ".vrt.lock") + ): yield @@ -73,7 +75,7 @@ def ensure_setup(root, folders=(), file_templates=(), force=False, **kwargs): path = os.path.join(root, relpath) if force or not os.path.exists(path): body = template.format(**kwargs) - with open(path, 'w') as file: + with open(path, "w") as file: file.write(body) created_files[path] = body @@ -81,9 +83,9 @@ def ensure_setup(root, folders=(), file_templates=(), force=False, **kwargs): def check_call_make(path, targets=(), variables=()): - make_targets = ' '.join(targets) + make_targets = " ".join(targets) variables_items = collections.OrderedDict(variables).items() - make_variables = ' '.join('%s="%s"' % (k.upper(), v) for k, v in variables_items) - cmd = 'make -C {path} {make_targets} {make_variables}'.format(**locals()) + make_variables = " ".join('%s="%s"' % (k.upper(), v) for k, v in variables_items) + cmd = "make -C {path} {make_targets} {make_variables}".format(**locals()) subprocess.check_call(cmd, shell=True) return cmd diff --git a/environment-minimal.in.yml b/environment-minimal.in.yml index bc4922e..499082a 100644 --- a/environment-minimal.in.yml +++ b/environment-minimal.in.yml @@ -1,11 +1,11 @@ channels: - - defaults - - conda-forge +- defaults +- conda-forge dependencies: - - appdirs - - click - - gdal - - make - - pip - - pip: - - fasteners +- appdirs +- click +- gdal +- make +- pip +- pip: + - fasteners diff --git a/environment.in.yml b/environment.in.yml index 1635008..36b534f 100644 --- a/environment.in.yml +++ b/environment.in.yml @@ -1,15 +1,15 @@ channels: - - defaults - - conda-forge +- defaults +- conda-forge dependencies: - - appdirs - - click - - fiona - - make - - pip - - pytest - - pytest-cov - - pytest-mock - - rasterio - - pip: - - fasteners +- appdirs +- click +- fiona +- make +- pip +- pytest +- pytest-cov +- pytest-mock +- rasterio +- pip: + - fasteners diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..8e4e722 --- /dev/null +++ b/environment.yml @@ -0,0 +1,16 @@ +# environment.yml: Mandatory dependencies only. +channels: +- conda-forge +- nodefaults +# EXAMPLE: +# dependencies: +# - package1 +# - package2 +# DO NOT EDIT ABOVE THIS LINE, ADD DEPENDENCIES BELOW AS SHOWN IN THE EXAMPLE +dependencies: +- appdirs +- click +- fasteners +- fiona +- gdal +- rasterio diff --git a/pyproject.toml b/pyproject.toml index 6fce0d9..bf5b767 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,30 +1,77 @@ [build-system] -requires = [ - "setuptools>=42", - "wheel", - "setuptools_scm[toml]>=3.4", - "setuptools_scm_git_archive", +requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] + +[project] +authors = [ + {name = "B-Open Solutions srl, Alessandro Amici", email = "info@bopen.eu"} +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython", + "Operating System :: OS Independent", + "License :: OSI Approved :: Apache Software License", + "Topic :: Scientific/Engineering :: GIS" ] +dependencies = ["appdirs", "click", "fasteners"] +description = "Python script to download global terrain digital elevation models, SRTM 30m DEM and SRTM 90m DEM." +dynamic = ["version"] +keywords = ["script", "download", "SRTM", "DEM", "DTM", "global", "digital", "elevation", "terrain", "model"] +license = {file = "LICENSE"} +name = "elevation" +readme = "README.rst" +urls = {documentation = "http://elevation.bopen.eu"} -[tool.setuptools_scm] -write_to = "elevation/version.py" -write_to_template = ''' -# don't change, don't track in version control -__version__ = "{version}" -''' +[project.scripts] +eio = "elevation.cli:eio" +[tool.coverage.run] +branch = true -[tool.black] -line-length = 105 -skip-string-normalization = true +[tool.mypy] +strict = false -[tool.pytest.ini_options] -norecursedirs = [ - "build", - "docs", - ".tox", +[tool.ruff] +ignore = [ + # pydocstyle: Missing Docstrings + "D1", + # pydocstyle: numpy convention + "D107", + "D203", + "D212", + "D213", + "D402", + "D413", + "D415", + "D416", + "D417" +] +# Black line length is 88, but black does not format comments. +line-length = 110 +select = [ + # pyflakes + "F", + # pycodestyle + "E", + "W", + # isort + "I", + # pydocstyle + "D" ] -[tool.coverage.run] -branch = true -omit = ["setup.py"] +[tool.setuptools] +packages = ["elevation"] + +[tool.setuptools_scm] +write_to = "elevation/version.py" +write_to_template = """ +# Do not change! Do not track in version control! +__version__ = "{version}" +""" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 647057a..0000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[zest.releaser] -python-file-with-version = elevation/version.py -create-wheel = yes diff --git a/setup.py b/setup.py deleted file mode 100644 index d4c0543..0000000 --- a/setup.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016-2021 B-Open Solutions srl - http://bopen.eu -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import codecs -import os - -from setuptools import find_packages, setup - - -def read(fname): - file_path = os.path.join(os.path.dirname(__file__), fname) - return codecs.open(file_path, encoding='utf-8').read() - - -setup( - name='elevation', - author='B-Open Solutions srl, Alessandro Amici', - author_email='info@bopen.eu', - license='Apache License Version 2.0', - url='http://elevation.bopen.eu', - description="Python script to download global terrain digital elevation models, " - "SRTM 30m DEM and SRTM 90m DEM.", - long_description=read('README.rst'), - long_description_content_type="text/x-rst", - packages=find_packages(), - include_package_data=True, - install_requires=['appdirs', 'click', 'fasteners'], - extras_require={'reference': ['fiona', 'rasterio']}, - zip_safe=True, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: Science/Research', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: Implementation :: CPython', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: Apache Software License', - 'Topic :: Scientific/Engineering :: GIS', - ], - keywords='script download SRTM DEM DTM global digital elevation terrain model', - entry_points={'console_scripts': ['eio=elevation.cli:eio']}, -) diff --git a/tests/environment-macos-3.8.yml b/tests/environment-macos-3.8.yml deleted file mode 100644 index 7f84512..0000000 --- a/tests/environment-macos-3.8.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: macos-3.8 -channels: - - defaults - - conda-forge -dependencies: - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - blas=1.0 - - boost-cpp=1.72.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - cairo=1.16.0 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.0 - - curl=7.69.1 - - expat=2.2.10 - - fiona=1.8.13 - - fontconfig=2.13.1 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.4 - - geos=3.8.1 - - geotiff=1.5.1 - - gettext=0.19.8.1 - - giflib=5.2.1 - - glib=2.66.1 - - hdf4=4.2.13 - - hdf5=1.10.5 - - icu=64.2 - - intel-openmp=2019.4 - - jpeg=9d - - json-c=0.13.1 - - kealib=1.4.13 - - krb5=1.17.1 - - libcurl=7.69.1 - - libcxx=10.0.0 - - libdap4=3.20.6 - - libedit=3.1.20191231 - - libffi=3.3 - - libgdal=3.0.4 - - libgfortran=3.0.1 - - libiconv=1.16 - - libkml=1.3.0 - - libnetcdf=4.7.4 - - libpng=1.6.37 - - libpq=12.2 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libtiff=4.1.0 - - libwebp-base=1.1.0 - - libxml2=2.9.10 - - lz4-c=1.9.2 - - make=4.2.1 - - mkl=2019.4 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - munch=2.5.0 - - ncurses=6.2 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.1 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - pixman=0.38.0 - - poppler=0.67.0 - - poppler-data=0.4.10 - - postgresql=12.2 - - proj=7.0.0 - - pyparsing=2.4.7 - - python=3.8.5 - - python_abi=3.8 - - rasterio=1.1.5 - - readline=8.0 - - setuptools=50.3.1 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.33.0 - - tbb=2020.3 - - tiledb=1.7.7 - - tk=8.6.10 - - wheel=0.35.1 - - xerces-c=3.2.2 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.4.5 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: /usr/local/miniconda/envs/macos-3.8 diff --git a/tests/environment-ubuntu-3.6.yml b/tests/environment-ubuntu-3.6.yml deleted file mode 100644 index afa4ac1..0000000 --- a/tests/environment-ubuntu-3.6.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: ubuntu-3.6 -channels: - - defaults - - conda-forge -dependencies: - - _libgcc_mutex=0.1 - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - blas=1.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - cairo=1.14.12 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.0 - - curl=7.67.0 - - expat=2.2.10 - - fiona=1.8.13.post1 - - fontconfig=2.13.0 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.2 - - geos=3.8.0 - - geotiff=1.5.1 - - giflib=5.1.4 - - glib=2.66.1 - - hdf4=4.2.13 - - hdf5=1.10.4 - - icu=58.2 - - intel-openmp=2020.2 - - jpeg=9b - - json-c=0.13.1 - - kealib=1.4.7 - - krb5=1.16.4 - - ld_impl_linux-64=2.33.1 - - libboost=1.67.0 - - libcurl=7.67.0 - - libdap4=3.19.1 - - libedit=3.1.20191231 - - libffi=3.3 - - libgcc-ng=9.1.0 - - libgdal=3.0.2 - - libgfortran-ng=7.3.0 - - libkml=1.3.0 - - libnetcdf=4.6.1 - - libpng=1.6.37 - - libpq=11.5 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libstdcxx-ng=9.1.0 - - libtiff=4.1.0 - - libuuid=1.0.3 - - libxcb=1.14 - - libxml2=2.9.10 - - lz4-c=1.8.1.2 - - make=4.2.1 - - mkl=2020.2 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - munch=2.5.0 - - ncurses=6.2 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.0 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - pixman=0.40.0 - - poppler=0.65.0 - - poppler-data=0.4.10 - - postgresql=11.5 - - proj=6.2.1 - - pyparsing=2.4.7 - - python=3.6.12 - - rasterio=1.1.0 - - readline=8.0 - - setuptools=50.3.1 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.33.0 - - tbb=2018.0.5 - - tiledb=1.6.3 - - tk=8.6.10 - - tzcode=2020a - - wheel=0.35.1 - - xerces-c=3.2.3 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.3.7 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: /usr/share/miniconda/envs/ubuntu-3.6 diff --git a/tests/environment-ubuntu-3.7.yml b/tests/environment-ubuntu-3.7.yml deleted file mode 100644 index 860d252..0000000 --- a/tests/environment-ubuntu-3.7.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: ubuntu-3.7 -channels: - - defaults - - conda-forge -dependencies: - - _libgcc_mutex=0.1 - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - blas=1.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - cairo=1.14.12 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.0 - - curl=7.67.0 - - expat=2.2.10 - - fiona=1.8.13.post1 - - fontconfig=2.13.0 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.2 - - geos=3.8.0 - - geotiff=1.5.1 - - giflib=5.1.4 - - glib=2.66.1 - - hdf4=4.2.13 - - hdf5=1.10.4 - - icu=58.2 - - intel-openmp=2020.2 - - jpeg=9b - - json-c=0.13.1 - - kealib=1.4.7 - - krb5=1.16.4 - - ld_impl_linux-64=2.33.1 - - libboost=1.67.0 - - libcurl=7.67.0 - - libdap4=3.19.1 - - libedit=3.1.20191231 - - libffi=3.3 - - libgcc-ng=9.1.0 - - libgdal=3.0.2 - - libgfortran-ng=7.3.0 - - libkml=1.3.0 - - libnetcdf=4.6.1 - - libpng=1.6.37 - - libpq=11.5 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libstdcxx-ng=9.1.0 - - libtiff=4.1.0 - - libuuid=1.0.3 - - libxcb=1.14 - - libxml2=2.9.10 - - lz4-c=1.8.1.2 - - make=4.2.1 - - mkl=2020.2 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - munch=2.5.0 - - ncurses=6.2 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.0 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - pixman=0.40.0 - - poppler=0.65.0 - - poppler-data=0.4.10 - - postgresql=11.5 - - proj=6.2.1 - - pyparsing=2.4.7 - - python=3.7.9 - - rasterio=1.1.0 - - readline=8.0 - - setuptools=50.3.1 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.33.0 - - tbb=2018.0.5 - - tiledb=1.6.3 - - tk=8.6.10 - - tzcode=2020a - - wheel=0.35.1 - - xerces-c=3.2.3 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.3.7 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: /usr/share/miniconda/envs/ubuntu-3.7 diff --git a/tests/environment-ubuntu-3.8-minimal.yml b/tests/environment-ubuntu-3.8-minimal.yml deleted file mode 100644 index 70bd863..0000000 --- a/tests/environment-ubuntu-3.8-minimal.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: ubuntu-3.8-minimal -channels: - - defaults - - conda-forge -dependencies: - - _libgcc_mutex=0.1 - - appdirs=1.4.4 - - blas=1.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - cairo=1.14.12 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - curl=7.67.0 - - expat=2.2.10 - - fontconfig=2.13.0 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.2 - - geos=3.8.0 - - geotiff=1.5.1 - - giflib=5.1.4 - - glib=2.66.1 - - hdf4=4.2.13 - - hdf5=1.10.4 - - icu=58.2 - - intel-openmp=2020.2 - - jpeg=9b - - json-c=0.13.1 - - kealib=1.4.7 - - krb5=1.16.4 - - ld_impl_linux-64=2.33.1 - - libboost=1.67.0 - - libcurl=7.67.0 - - libdap4=3.19.1 - - libedit=3.1.20191231 - - libffi=3.3 - - libgcc-ng=9.1.0 - - libgdal=3.0.2 - - libgfortran-ng=7.3.0 - - libkml=1.3.0 - - libnetcdf=4.6.1 - - libpng=1.6.37 - - libpq=11.5 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libstdcxx-ng=9.1.0 - - libtiff=4.1.0 - - libuuid=1.0.3 - - libxcb=1.14 - - libxml2=2.9.10 - - lz4-c=1.8.1.2 - - make=4.2.1 - - mkl=2020.2 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - ncurses=6.2 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.0 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - pixman=0.40.0 - - poppler=0.65.0 - - poppler-data=0.4.10 - - postgresql=11.5 - - proj=6.2.1 - - python=3.8.5 - - readline=8.0 - - setuptools=50.3.1 - - six=1.15.0 - - sqlite=3.33.0 - - tbb=2018.0.5 - - tiledb=1.6.3 - - tk=8.6.10 - - tzcode=2020a - - wheel=0.35.1 - - xerces-c=3.2.3 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.3.7 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: /usr/share/miniconda/envs/ubuntu-3.8-minimal diff --git a/tests/environment-ubuntu-3.8.yml b/tests/environment-ubuntu-3.8.yml deleted file mode 100644 index 2a68c4a..0000000 --- a/tests/environment-ubuntu-3.8.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: ubuntu-3.8 -channels: - - defaults - - conda-forge -dependencies: - - _libgcc_mutex=0.1 - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - blas=1.0 - - boost-cpp=1.72.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - cairo=1.16.0 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.0 - - curl=7.69.1 - - expat=2.2.10 - - fiona=1.8.13.post1 - - fontconfig=2.13.1 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.4 - - geos=3.8.1 - - geotiff=1.6.0 - - giflib=5.2.1 - - glib=2.66.1 - - hdf4=4.2.13 - - hdf5=1.10.6 - - icu=64.2 - - intel-openmp=2020.2 - - jpeg=9d - - json-c=0.13.1 - - kealib=1.4.13 - - krb5=1.17.1 - - ld_impl_linux-64=2.33.1 - - libcurl=7.69.1 - - libdap4=3.20.6 - - libedit=3.1.20191231 - - libffi=3.3 - - libgcc-ng=9.1.0 - - libgdal=3.0.4 - - libgfortran-ng=7.3.0 - - libiconv=1.15 - - libkml=1.3.0 - - libnetcdf=4.7.4 - - libpng=1.6.37 - - libpq=12.2 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libstdcxx-ng=9.1.0 - - libtiff=4.1.0 - - libuuid=2.32.1 - - libwebp-base=1.1.0 - - libxcb=1.14 - - libxml2=2.9.10 - - lz4-c=1.9.2 - - make=4.2.1 - - mkl=2020.2 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - munch=2.5.0 - - ncurses=6.2 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.1 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - pixman=0.38.0 - - poppler=0.87.0 - - poppler-data=0.4.10 - - postgresql=12.2 - - proj=7.0.0 - - pyparsing=2.4.7 - - python=3.8.5 - - python_abi=3.8 - - rasterio=1.1.5 - - readline=8.0 - - setuptools=50.3.1 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.33.0 - - tbb=2020.3 - - tiledb=1.7.7 - - tk=8.6.10 - - wheel=0.35.1 - - xerces-c=3.2.2 - - xorg-kbproto=1.0.7 - - xorg-libice=1.0.10 - - xorg-libsm=1.2.3 - - xorg-libx11=1.6.12 - - xorg-libxext=1.3.4 - - xorg-libxrender=0.9.10 - - xorg-renderproto=0.11.1 - - xorg-xextproto=7.3.0 - - xorg-xproto=7.0.31 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.4.5 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: /usr/share/miniconda/envs/ubuntu-3.8 diff --git a/tests/environment-ubuntu-3.9.yml b/tests/environment-ubuntu-3.9.yml deleted file mode 100644 index 382011e..0000000 --- a/tests/environment-ubuntu-3.9.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: ubuntu-3.9 -channels: - - defaults - - conda-forge -dependencies: - - _libgcc_mutex=0.1 - - _openmp_mutex=4.5 - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - boost-cpp=1.74.0 - - bzip2=1.0.8 - - c-ares=1.17.1 - - ca-certificates=2021.1.19 - - cairo=1.16.0 - - certifi=2020.12.5 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.1 - - coverage=5.5 - - curl=7.71.1 - - expat=2.3.0 - - fiona=1.8.18 - - fontconfig=2.13.1 - - freetype=2.10.4 - - freexl=1.0.6 - - gdal=3.2.1 - - geos=3.9.0 - - geotiff=1.6.0 - - gettext=0.21.0 - - giflib=5.2.1 - - glib=2.68.0 - - glib-tools=2.68.0 - - hdf4=4.2.13 - - hdf5=1.10.6 - - icu=68.1 - - iniconfig=1.1.1 - - jpeg=9d - - json-c=0.13.1 - - kealib=1.4.14 - - krb5=1.17.1 - - ld_impl_linux-64=2.33.1 - - libblas=3.8.0 - - libcblas=3.8.0 - - libcurl=7.71.1 - - libdap4=3.20.6 - - libedit=3.1.20210216 - - libev=4.33 - - libffi=3.3 - - libgcc-ng=9.3.0 - - libgdal=3.2.1 - - libgfortran-ng=7.3.0 - - libglib=2.68.0 - - libgomp=9.3.0 - - libiconv=1.16 - - libkml=1.3.0 - - liblapack=3.8.0 - - libnetcdf=4.7.4 - - libnghttp2=1.41.0 - - libopenblas=0.3.10 - - libpng=1.6.37 - - libpq=12.3 - - librttopo=1.1.0 - - libspatialite=5.0.1 - - libssh2=1.9.0 - - libstdcxx-ng=9.3.0 - - libtiff=4.2.0 - - libuuid=2.32.1 - - libwebp-base=1.2.0 - - libxcb=1.14 - - libxml2=2.9.10 - - lz4-c=1.9.3 - - make=4.2.1 - - more-itertools=8.7.0 - - munch=2.5.0 - - ncurses=6.2 - - numpy=1.20.2 - - openjpeg=2.4.0 - - openssl=1.1.1k - - packaging=20.9 - - pcre=8.44 - - pip=21.0.1 - - pixman=0.40.0 - - pluggy=0.13.1 - - poppler=0.89.0 - - poppler-data=0.4.10 - - postgresql=12.3 - - proj=7.2.0 - - py=1.10.0 - - pyparsing=2.4.7 - - pytest=6.2.3 - - pytest-cov=2.11.1 - - pytest-mock=3.5.1 - - python=3.9.2 - - python_abi=3.9 - - rasterio=1.2.2 - - readline=8.1 - - setuptools=52.0.0 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.35.4 - - tiledb=2.2.7 - - tk=8.6.10 - - toml=0.10.2 - - tzcode=2021a - - tzdata=2020f - - wheel=0.36.2 - - xerces-c=3.2.3 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.4.9 - - pip: - - fasteners==0.16 -prefix: /usr/share/miniconda/envs/ubuntu-3.9 diff --git a/tests/environment-windows-3.8.yml b/tests/environment-windows-3.8.yml deleted file mode 100644 index 8ff6b9d..0000000 --- a/tests/environment-windows-3.8.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: windows-3.8 -channels: - - defaults - - conda-forge -dependencies: - - affine=2.3.0 - - appdirs=1.4.4 - - attrs=20.3.0 - - blas=1.0 - - bzip2=1.0.8 - - ca-certificates=2020.10.14 - - certifi=2020.6.20 - - cfitsio=3.470 - - click=7.1.2 - - click-plugins=1.1.1 - - cligj=0.7.0 - - curl=7.69.1 - - expat=2.2.10 - - fiona=1.8.13.post1 - - freetype=2.10.4 - - freexl=1.0.5 - - gdal=3.0.4 - - geos=3.8.1 - - geotiff=1.5.1 - - gettext=0.19.8.1 - - glib=2.65.0 - - hdf4=4.2.13 - - hdf5=1.10.6 - - icu=64.2 - - intel-openmp=2020.2 - - jpeg=9d - - kealib=1.4.13 - - krb5=1.17.1 - - libboost=1.67.0 - - libcurl=7.69.1 - - libffi=3.2.1 - - libgdal=3.0.4 - - libiconv=1.15 - - libkml=1.3.0 - - libnetcdf=4.7.4 - - libpng=1.6.37 - - libpq=12.2 - - libspatialite=4.3.0a - - libssh2=1.9.0 - - libtiff=4.1.0 - - libwebp-base=1.1.0 - - libxml2=2.9.10 - - lz4-c=1.9.2 - - m2w64-expat=2.1.1 - - m2w64-gcc-libgfortran=5.3.0 - - m2w64-gcc-libs=5.3.0 - - m2w64-gcc-libs-core=5.3.0 - - m2w64-gettext=0.19.7 - - m2w64-gmp=6.1.0 - - m2w64-libiconv=1.14 - - m2w64-libwinpthread-git=5.0.0.4634.697f757 - - m2w64-xz=5.2.2 - - make=4.3 - - mkl=2020.2 - - mkl-service=2.3.0 - - mkl_fft=1.2.0 - - mkl_random=1.1.1 - - msys2-conda-epoch=20160418 - - munch=2.5.0 - - numpy=1.19.2 - - numpy-base=1.19.2 - - openjpeg=2.3.1 - - openssl=1.1.1h - - pcre=8.44 - - pip=20.2.4 - - poppler=0.87.0 - - poppler-data=0.4.10 - - postgresql=12.2 - - proj=7.0.0 - - pyparsing=2.4.7 - - python=3.8.5 - - python_abi=3.8 - - rasterio=1.1.5 - - setuptools=50.3.1 - - shapely=1.7.1 - - six=1.15.0 - - snuggs=1.4.7 - - sqlite=3.33.0 - - tbb=2020.3 - - tiledb=1.7.7 - - tk=8.6.10 - - vc=14.1 - - vs2015_runtime=14.16.27012 - - wheel=0.35.1 - - wincertstore=0.2 - - xerces-c=3.2.2 - - xz=5.2.5 - - zlib=1.2.11 - - zstd=1.4.5 - - pip: - - fasteners==0.15 - - monotonic==1.5 -prefix: C:\Miniconda\envs\windows-3.8 diff --git a/tests/test_cli.py b/tests/test_cli.py index df31f92..f8ac4cc 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -12,7 +12,7 @@ def test_eio_selfcheck(mocker): runner = click.testing.CliRunner() - mocker.patch('subprocess.check_output') + mocker.patch("subprocess.check_output") result = runner.invoke(cli.selfcheck) assert not result.exception assert subprocess.check_output.call_count == len(cli.elevation.TOOLS) @@ -21,85 +21,85 @@ def test_eio_selfcheck(mocker): def test_click_merge_parent_params(): runner = click.testing.CliRunner() - @cli.eio.command('return_kwargs') + @cli.eio.command("return_kwargs") @cli.click_merge_parent_params def return_kwargs(**kwargs): print(kwargs) - result = runner.invoke(cli.eio, 'return_kwargs'.split()) + result = runner.invoke(cli.eio, "return_kwargs".split()) assert not result.exception - assert 'product' in result.output and 'cache_dir' in result.output + assert "product" in result.output and "cache_dir" in result.output result = runner.invoke(return_kwargs) assert not result.exception - assert result.output == '{}\n' + assert result.output == "{}\n" def test_eio_info(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s info' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s info" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 1 def test_eio_seed(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s seed --bounds 12.5 42 12.5 42' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s seed --bounds 12.5 42 12.5 42" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 2 def test_eio_clip(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s clip --bounds 12.5 42 12.5 42' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s clip --bounds 12.5 42 12.5 42" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 4 - mocker.patch('subprocess.check_call') - result = runner.invoke(cli.eio, ['clip']) + mocker.patch("subprocess.check_call") + result = runner.invoke(cli.eio, ["clip"]) assert result.exception assert subprocess.check_call.call_count == 0 - mocker.patch('subprocess.check_call') - result = runner.invoke(cli.eio, 'clip --reference .'.split()) + mocker.patch("subprocess.check_call") + result = runner.invoke(cli.eio, "clip --reference .".split()) assert result.exception assert subprocess.check_call.call_count == 0 def test_eio_clean(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s clean' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s clean" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 1 def test_eio_distclean(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s distclean' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s distclean" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 1 def test_eio(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") runner = click.testing.CliRunner() - options = '--cache_dir %s seed --bounds 12.5 42 12.5 42' % str(root) - mocker.patch('subprocess.check_call') + options = "--cache_dir %s seed --bounds 12.5 42 12.5 42" % str(root) + mocker.patch("subprocess.check_call") result = runner.invoke(cli.eio, options.split()) assert not result.exception assert subprocess.check_call.call_count == 2 diff --git a/tests/test_datasource.py b/tests/test_datasource.py index 6bca1b2..2a0ab64 100644 --- a/tests/test_datasource.py +++ b/tests/test_datasource.py @@ -21,86 +21,117 @@ def test_srtm3_tile_ilonlat(): def test_srtm1_tiles_names(): - assert list(datasource.srtm1_tiles_names(10.1, 44.9, 10.1, 44.9)) == ['N44/N44E010.tif'] + assert list(datasource.srtm1_tiles_names(10.1, 44.9, 10.1, 44.9)) == [ + "N44/N44E010.tif" + ] # NOTE this also tests int (not float) input - assert list(datasource.srtm1_tiles_names(10, 44, 11, 45)) == ['N44/N44E010.tif'] + assert list(datasource.srtm1_tiles_names(10, 44, 11, 45)) == ["N44/N44E010.tif"] def test_srtm3_tiles_names(): - assert next(datasource.srtm3_tiles_names(10.1, 44.9, 10.1, 44.9)).endswith('srtm_39_04.tif') - assert next(datasource.srtm3_tiles_names(25.50, 58.40, 27.67, 60.06)).endswith('srtm_42_01.tif') + assert next(datasource.srtm3_tiles_names(10.1, 44.9, 10.1, 44.9)).endswith( + "srtm_39_04.tif" + ) + assert next(datasource.srtm3_tiles_names(25.50, 58.40, 27.67, 60.06)).endswith( + "srtm_42_01.tif" + ) assert len(list(datasource.srtm3_tiles_names(9.9, 39.1, 15.1, 45.1))) == 9 def test_srtm_ellip_tiles_names(): # Check the various subdirs in srtm_ellip - ds1 = ['North/North_30_60/N44E010_wgs84.tif'] - ds2 = ['North/North_0_29/N07W074_wgs84.tif'] - ds3 = ['South/S20E015_wgs84.tif'] + ds1 = ["North/North_30_60/N44E010_wgs84.tif"] + ds2 = ["North/North_0_29/N07W074_wgs84.tif"] + ds3 = ["South/S20E015_wgs84.tif"] assert list(datasource.srtm_ellip_tiles_names(10.1, 44.9, 10.1, 44.9)) == ds1 assert list(datasource.srtm_ellip_tiles_names(-73.99, 7.056, -73.90, 7.660)) == ds2 - assert list(datasource.srtm_ellip_tiles_names(15.931, -19.194, 15.329, -19.961)) == ds3 + assert ( + list(datasource.srtm_ellip_tiles_names(15.931, -19.194, 15.329, -19.961)) == ds3 + ) def test_ensure_tiles(mocker): - mocker.patch('subprocess.check_call') - cmd = datasource.ensure_tiles('/tmp', ['a', 'b']) + mocker.patch("subprocess.check_call") + cmd = datasource.ensure_tiles("/tmp", ["a", "b"]) assert cmd == 'make -C /tmp download ENSURE_TILES="a b"' subprocess.check_call.assert_called_once_with(cmd, shell=True) def test_do_clip(mocker): bounds = (1, 5, 2, 6) - mocker.patch('subprocess.check_call') - cmd = datasource.do_clip(path='/tmp', bounds=bounds, output='/out.tif') - assert cmd.startswith('make -C /tmp clip OUTPUT="/out.tif" PROJWIN="1 6 2 5" RUN_ID="') + mocker.patch("subprocess.check_call") + cmd = datasource.do_clip(path="/tmp", bounds=bounds, output="/out.tif") + assert cmd.startswith( + 'make -C /tmp clip OUTPUT="/out.tif" PROJWIN="1 6 2 5" RUN_ID="' + ) subprocess.check_call.assert_called_with(cmd, shell=True) def test_seed(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") bounds = (13.1, 43.1, 13.9, 43.9) - mocker.patch('subprocess.check_call') - datasource.seed(cache_dir=str(root), product='SRTM1', bounds=bounds) + mocker.patch("subprocess.check_call") + datasource.seed(cache_dir=str(root), product="SRTM1", bounds=bounds) assert len(root.listdir()) == 1 datasource_root = root.listdir()[0] - expected_cmd = 'make -C %s download ENSURE_TILES="N43/N43E013.tif"' % datasource_root + expected_cmd = ( + 'make -C %s download ENSURE_TILES="N43/N43E013.tif"' % datasource_root + ) subprocess.check_call.assert_any_call(expected_cmd, shell=True) with pytest.raises(RuntimeError): - datasource.seed(cache_dir=str(root), product='SRTM1', bounds=(-180, -90, 180, 90)) + datasource.seed( + cache_dir=str(root), product="SRTM1", bounds=(-180, -90, 180, 90) + ) def test_build_bounds(): raw_bounds = (13.1, 43.1, 13.9, 43.9) - assert datasource.build_bounds(raw_bounds, margin='0') == raw_bounds - - assert datasource.build_bounds(raw_bounds, margin='0.08') == (13.02, 43.02, 13.98, 43.98) - assert datasource.build_bounds(raw_bounds, margin='10%') == (13.02, 43.02, 13.98, 43.98) + assert datasource.build_bounds(raw_bounds, margin="0") == raw_bounds + + assert datasource.build_bounds(raw_bounds, margin="0.08") == ( + 13.02, + 43.02, + 13.98, + 43.98, + ) + assert datasource.build_bounds(raw_bounds, margin="10%") == ( + 13.02, + 43.02, + 13.98, + 43.98, + ) def test_clip(mocker, tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") bounds = (13.1, 43.1, 14.9, 44.9) class UUID: - hex = 'asd' + hex = "asd" uuid_mock = mocker.Mock() uuid_mock.return_value = UUID - mocker.patch('uuid.uuid4', uuid_mock) - mocker.patch('subprocess.check_call') - datasource.clip(cache_dir=str(root), product='SRTM1', bounds=bounds, output='out.tif') + mocker.patch("uuid.uuid4", uuid_mock) + mocker.patch("subprocess.check_call") + datasource.clip( + cache_dir=str(root), product="SRTM1", bounds=bounds, output="out.tif" + ) assert len(root.listdir()) == 1 datasource_root = root.listdir()[0] - cmd = 'make -C %s clip OUTPUT="out.tif" PROJWIN="13.1 44.9 14.9 43.1" RUN_ID="asd"' % datasource_root + cmd = ( + 'make -C %s clip OUTPUT="out.tif" PROJWIN="13.1 44.9 14.9 43.1" RUN_ID="asd"' + % datasource_root + ) subprocess.check_call.assert_any_call(cmd, shell=True) def test_clean(mocker, tmpdir): - root = tmpdir.join('root') - mocker.patch('subprocess.check_call') - datasource.clean(cache_dir=str(root), product='SRTM1') + root = tmpdir.join("root") + mocker.patch("subprocess.check_call") + datasource.clean(cache_dir=str(root), product="SRTM1") assert len(root.listdir()) == 1 datasource_root = root.listdir()[0] - subprocess.check_call.assert_any_call('make -C %s clean ' % datasource_root, shell=True) + subprocess.check_call.assert_any_call( + "make -C %s clean " % datasource_root, shell=True + ) diff --git a/tests/test_util.py b/tests/test_util.py index 0ea7469..16bcbab 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -10,57 +10,57 @@ def test_selfcheck(): - assert 'NAME' not in util.selfcheck([('NAME', 'true')]) - assert 'NAME' in util.selfcheck([('NAME', 'false')]) + assert "NAME" not in util.selfcheck([("NAME", "true")]) + assert "NAME" in util.selfcheck([("NAME", "false")]) def test_lock_tiles(tmpdir, mocker): - root = str(tmpdir.join('root')) - with util.lock_tiles(root, ['a.tiff']): - assert os.path.exists(os.path.join(root, 'cache', 'a.tiff.lock')) + root = str(tmpdir.join("root")) + with util.lock_tiles(root, ["a.tiff"]): + assert os.path.exists(os.path.join(root, "cache", "a.tiff.lock")) def test_lock_vrt(tmpdir, mocker): - root = str(tmpdir.join('root')) + root = str(tmpdir.join("root")) - with util.lock_vrt(root, 'SRTM1'): - assert os.path.exists(os.path.join(root, 'SRTM1.vrt.lock')) + with util.lock_vrt(root, "SRTM1"): + assert os.path.exists(os.path.join(root, "SRTM1.vrt.lock")) def test_ensure_setup(tmpdir): - root = tmpdir.join('root') + root = tmpdir.join("root") root_path = str(root) created_folders, _ = util.ensure_setup(root_path) assert len(created_folders) == 0 assert len(tmpdir.listdir()) == 1 - folders = ['etc', 'lib'] + folders = ["etc", "lib"] created_folders, _ = util.ensure_setup(root_path, folders=folders) assert len(created_folders) == 2 - assert created_folders[0].endswith('etc') - assert created_folders[1].endswith('lib') + assert created_folders[0].endswith("etc") + assert created_folders[1].endswith("lib") assert len(root.listdir()) == 3 - file_templates = [('Makefile', 'all: {target}')] + file_templates = [("Makefile", "all: {target}")] created_folders, created_files = util.ensure_setup( - root_path, folders=folders, file_templates=file_templates, target='file.txt' + root_path, folders=folders, file_templates=file_templates, target="file.txt" ) assert len(created_folders) == 0 assert len(created_files) == 1 assert len(root.listdir()) == 4 - assert root.join('Makefile').read() == 'all: file.txt' + assert root.join("Makefile").read() == "all: file.txt" created_folders, created_files = util.ensure_setup( - root_path, folders=folders, file_templates=file_templates, target='wrong' + root_path, folders=folders, file_templates=file_templates, target="wrong" ) assert len(created_folders) == 0 assert len(created_files) == 0 assert len(root.listdir()) == 4 - assert root.join('Makefile').read() == 'all: file.txt' + assert root.join("Makefile").read() == "all: file.txt" def test_check_call_make(mocker): - mocker.patch('subprocess.check_call') - cmd = util.check_call_make('/tmp') - assert cmd.strip() == 'make -C /tmp' + mocker.patch("subprocess.check_call") + cmd = util.check_call_make("/tmp") + assert cmd.strip() == "make -C /tmp" subprocess.check_call.assert_called_once_with(cmd, shell=True)