From 0055bcbb83725973595b18c7655f0710ab0971f4 Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Thu, 11 May 2023 07:35:29 -0600 Subject: [PATCH] feat: use pyproject.toml Includes a pre-commit autoupdate. No functional code changes. --- .github/setup/action.yml | 2 +- .github/workflows/continuous-integration.yml | 10 +- .pre-commit-config.yaml | 7 +- CHANGELOG.md | 4 + README.md | 18 ++-- docker/Dockerfile | 12 +-- mypy.ini | 22 ---- pyproject.toml | 102 +++++++++++++++++++ requirements-dev.txt | 23 ----- scripts/install-min-requirements | 19 ++-- scripts/update | 3 +- setup.cfg | 66 ------------ src/stactools/cli/commands/lint.py | 2 +- src/stactools/core/io/xml.py | 8 +- 14 files changed, 143 insertions(+), 155 deletions(-) delete mode 100644 mypy.ini delete mode 100644 requirements-dev.txt delete mode 100644 setup.cfg diff --git a/.github/setup/action.yml b/.github/setup/action.yml index d8032ddd..3bbf5f8e 100644 --- a/.github/setup/action.yml +++ b/.github/setup/action.yml @@ -17,7 +17,7 @@ runs: uses: actions/cache@v2 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('setup.cfg', 'requirements-dev.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }} restore-keys: ${{ runner.os }}-pip- - name: Set up pre-commit cache uses: actions/cache@v2 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b885f72f..59d6d520 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -44,9 +44,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Install with extra_requires - run: pip install '.[all]' - - name: Install development dependencies - run: pip install -r requirements-dev.txt + run: pip install .[s3,dev] - name: Test run: ./scripts/test minimum-versions: @@ -63,12 +61,10 @@ jobs: - uses: ./.github/setup with: python-version: ${{ matrix.python-version }} - - name: Install development dependencies - run: pip install -r requirements-dev.txt + - name: Install the package + run: pip install .[dev] - name: Install the minimum requirements run: scripts/install-min-requirements - - name: Install the package - run: pip install . - name: Test run: scripts/test pre-release-versions: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cec6e04d..6b30a7c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,11 +3,12 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.258 + rev: v0.0.265 hooks: - id: ruff + args: [--fix, --exit-non-zero-on-fix] - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black - repo: https://github.com/codespell-project/codespell @@ -21,7 +22,7 @@ repos: hooks: - id: doc8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.3.0 hooks: - id: mypy # TODO lint test and scripts too diff --git a/CHANGELOG.md b/CHANGELOG.md index f874c02b..c386dfcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Use `pyproject.toml` for project metadata ([#424](https://github.com/stac-utils/stactools/pull/424)) + ## [0.4.7] - 2023-05-08 ### Changed diff --git a/README.md b/README.md index 6dc853e0..2cff53db 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ docker pull ghcr.io/stac-utils/stactools:latest stac --help ``` -### Docker +### Running from docker ```sh docker run --rm ghcr.io/stac-utils/stactools:latest --help @@ -80,7 +80,7 @@ docker run --rm ghcr.io/stac-utils/stactools:latest --help ## Documentation -See the [documentation page](https://stactools.readthedocs.io/en/latest/) for the latest docs. +See the [documentation page](https://stactools.readthedocs.io/) for the latest docs. ## Packages @@ -118,15 +118,12 @@ Third-party packages can be installed in the same way, or, if they are not on Py ## Developing -Basic development can be done with your system's default Python, though it it recommended to use a virtual environment. -E.g.: +Clone the repository and install it in editable mode with the `dev` optional dependencies: ```sh git clone https://github.com/stac-utils/stactools.git cd stactools -python -m venv venv -pip install -e . # install stactools into the virtual environment in editable mode -pip install -r requirements-dev.txt # install development requirements +pip install -e '.[dev]' ``` Linting and formatting are handled with [pre-commit](https://pre-commit.com/). @@ -205,13 +202,12 @@ conda activate stactools Finally, install `stactools` in editable mode and all development requirements: ```sh -pip install -e . -pip install -r requirements-dev.txt +pip install -e '.[dev]' ``` -### Documentation +### Developing the docs -To build and serve the docs, the development requirements must be installed with `pip install -r requirements-dev.txt`. +To build and serve the docs, the development requirements must be installed with `pip install -e '.[dev]'`. To build the docs, you can use `make html` from inside of the docs directory, and to build the docs and start a server that watches for changes, use `make livehtml`: ```sh diff --git a/docker/Dockerfile b/docker/Dockerfile index f7588d7a..4bece618 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -14,10 +14,10 @@ FROM base as dep_builder RUN apt-get update \ && apt-get install -y gcc build-essential \ && rm -rf /var/lib/apt/lists/* -COPY pyproject.toml setup.cfg ./ +COPY pyproject.toml ./ COPY src/stactools/core/__init__.py src/stactools/core/ # Install dependencies but remove the actual package -RUN pip install --prefix=/install .[all] \ +RUN pip install --prefix=/install .[s3] \ && rm -r /install/lib/*/site-packages/stactools* @@ -28,16 +28,14 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* COPY --from=dep_builder /install /opt/conda RUN conda install -c conda-forge pandoc && conda clean -af -COPY requirements-dev.txt ./ -RUN pip install -r requirements-dev.txt COPY . ./ # pre-commit run --all-files fails w/o this line RUN git init -RUN pip install -e .[all] +RUN pip install -e .[dev,s3] FROM base AS main COPY --from=dep_builder /install /opt/conda COPY src ./src -COPY pyproject.toml setup.cfg ./ -RUN pip install .[all] +COPY pyproject.toml ./ +RUN pip install .[s3] diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 67e3ad39..00000000 --- a/mypy.ini +++ /dev/null @@ -1,22 +0,0 @@ -[mypy] -mypy_path = src -explicit_package_bases = True -namespace_packages = True -show_error_codes = True -strict = True -warn_unused_ignores = False - -[mypy-fsspec.*] -ignore_missing_imports = True - -[mypy-osgeo.*] -ignore_missing_imports = True - -[mypy-rasterio.*] -ignore_missing_imports = True - -[mypy-s3fs.*] -ignore_missing_imports = True - -[mypy-shapely.*] -ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index fd65b944..930d2964 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,108 @@ +[project] +name = "stactools" +description = "Command line tool and Python library for working with STAC" +readme = "README.md" +authors = [ + { name = "Rob Emanuele", email = "rdemanuele@gmail.com" }, + { name = "Pete Gadomski", email = "pete.gadomski@gmail.com" }, +] +maintainers = [{ name = "Pete Gadomski", email = "pete.gadomski@gmail.com" }] +license = { text = "Apache-2.0" } +keywords = [ + "pystac", + "imagery", + "raster", + "catalog", + "STAC", +] +classifiers = [ + "Development Status :: 4 - Beta", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] +requires-python = ">=3.8" +dependencies = [ + "Shapely>=1.8.5.post1", + "aiohttp>=3.8.3", + "click>=8.1.3", + "fsspec>=2021.7", + "lxml>=4.9.2", + "numpy>=1.22.0", + "pyproj>=3.3", + "pystac[validation]>=1.6.1", + "rasterio>=1.3.2", + "requests>=2.27.1", + "stac-check>=1.3.2", + "stac-validator>=3.1.0", +] +dynamic = ["version"] + +[project.urls] +homepage = "https://github.com/stac-utils/stactools" +documentation = "https://stactools.readthedocs.io/" +repository = "https://github.com/stac-utils/stactools.git" +changelog = "https://github.com/stac-utils/stactools/blob/main/CHANGELOG.md" +discussions = "https://github.com/radiantearth/stac-spec/discussions/categories/stac-software" + +[project.scripts] +stac = "stactools.cli.cli:run_cli" + +[project.optional-dependencies] +dev = [ + "black~=23.3", + "codespell~=2.2", + "importlib-metadata~=6.6", + "ipython~=8.12", + "jupyter~=1.0", + "lxml-stubs~=0.4", + "mypy~=1.3", + "nbsphinx~=0.9", + "packaging~=23.1", + "pre-commit~=3.3", + "pydata-sphinx-theme~=0.13", + "pylint~=2.17", + "pytest~=7.3", + "pytest-cov~=3.0", + "ruff==0.0.265", + "sphinx~=7.0", + "sphinx-autobuild==2021.3.14", + "sphinx-click~=4.4", + "sphinxcontrib-napoleon~=0.7", + "types-certifi~=2021.10.8", + "types-orjson~=3.6", + "types-python-dateutil~=2.8", + "types-requests~=2.30", +] +s3 = ["s3fs>=2021.7"] + [build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" +[tool.setuptools.dynamic] +version = { attr = "stactools.core.__version__" } + [tool.ruff] line-length = 88 +select = ["E", "F", "I"] + +[tool.mypy] +mypy_path = "src" +explicit_package_bases = true +namespace_packages = true +show_error_codes = true +strict = true +warn_unused_ignores = true + +[[tool.mypy.overrides]] +module = [ + "fsspec", + "osgeo", + "rasterio", + "s3fs", + "shapely" +] +ignore_missing_imports = true diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 7bf7d4f1..00000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,23 +0,0 @@ -black -codespell -importlib-metadata -ipython -jupyter -lxml-stubs -mypy >= 0.981 # https://github.com/python/mypy/issues/13627 -nbsphinx -packaging -pre-commit -pydata-sphinx-theme -pylint -pytest -pytest-cov -ruff -sphinx < 6 -sphinx-autobuild -sphinx-click -sphinxcontrib-napoleon -types-certifi -types-orjson -types-python-dateutil -types-requests diff --git a/scripts/install-min-requirements b/scripts/install-min-requirements index 06f522da..a2849462 100755 --- a/scripts/install-min-requirements +++ b/scripts/install-min-requirements @@ -3,8 +3,8 @@ """Installs the minimum version of all stactools dependencies, with pip. Assumptions: -- You've installed the development dependencies: `pip install -r requirements-dev.txt` -- All of the dependencies in setup.cfg are specified with `>=` +- You've installed the development dependencies: `pip install '.[dev]'` +- All of the dependencies in pyproject.toml are specified with `>=` For more context on the approach and rationale behind testing against minimum requirements, see @@ -13,18 +13,25 @@ https://www.gadom.ski/2022/02/18/dependency-protection-with-python-and-github-ac """ import subprocess -from configparser import ConfigParser +import sys from pathlib import Path from packaging.requirements import Requirement +assert sys.version_info[0] == 3 +if sys.version_info[1] < 11: + import tomli as toml +else: + import tomllib as toml + + root = Path(__file__).parents[1] -setup_cfg = ConfigParser() -setup_cfg.read(root / "setup.cfg") +with open(root / "pyproject.toml", "rb") as f: + pyproject_toml = toml.load(f) requirements = [] for install_requires in filter( bool, - (i.strip() for i in setup_cfg["options"]["install_requires"].splitlines()), + (i.strip() for i in pyproject_toml["project"]["dependencies"]), ): requirement = Requirement(install_requires) assert len(requirement.specifier) == 1 diff --git a/scripts/update b/scripts/update index fe3e3f9a..95bf5239 100755 --- a/scripts/update +++ b/scripts/update @@ -18,7 +18,6 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then usage else python -m pip install --upgrade pip - pip install . - pip install -r requirements-dev.txt + pip install '.[dev]' fi fi diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 33824d29..00000000 --- a/setup.cfg +++ /dev/null @@ -1,66 +0,0 @@ -[metadata] -name = stactools -version = attr: stactools.core.__version__ -description = Command line tool and Python library for working with STAC -long_description = file: README.md -long_description_content_type = text/markdown -author = stac-utils -author_email = stac@radiant.earth -url = https://github.com/stac-utils/stactools -project_urls = - Documentation = https://stactools.readthedocs.io/en/latest/ - Issues = https://github.com/stac-utils/stactools/issues -keywords = - stactools - pystac - imagery - raster - catalog - STAC -classifiers = - Development Status :: 4 - Beta - License :: OSI Approved :: Apache Software License - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - -[options] -package_dir = - = src -packages = find_namespace: -zip_safe = False -include_package_data = True -python_requires = >= 3.8 -install_requires = - Shapely >= 1.8.5.post1 - aiohttp >= 3.8.3 - click >= 8.1.3 - fsspec >= 2021.7 - lxml >= 4.9.2 - numpy >= 1.22.0 - pyproj >= 3.3 - pystac[validation] >= 1.6.1 - rasterio >= 1.3.2 - requests >= 2.27.1 - stac-check >= 1.3.2 - stac-validator >= 3.1.0 - -[options.extras_require] -all = - %(s3)s -s3 = - s3fs >= 2021.7 - -[options.packages.find] -where = src - -[options.package_data] -* = py.typed - -[options.entry_points] -console_scripts = - stac = stactools.cli.cli:run_cli - -[global] -no-binary = rasterio diff --git a/src/stactools/cli/commands/lint.py b/src/stactools/cli/commands/lint.py index 7b13e04e..fc59fa30 100644 --- a/src/stactools/cli/commands/lint.py +++ b/src/stactools/cli/commands/lint.py @@ -2,7 +2,7 @@ from typing import Optional import click -from stac_check.lint import Linter # type: ignore +from stac_check.lint import Linter def create_lint_command(cli: click.Group) -> click.Command: diff --git a/src/stactools/core/io/xml.py b/src/stactools/core/io/xml.py index 66a2467c..584a4360 100644 --- a/src/stactools/core/io/xml.py +++ b/src/stactools/core/io/xml.py @@ -3,7 +3,6 @@ from lxml import etree from lxml.etree import _Element as lxmlElement - from stactools.core.io import ReadHrefModifier, read_text @@ -29,7 +28,7 @@ def find(self, xpath: str) -> Optional["XmlElement"]: Returns: Optional[XmlElement]: The found element, or None if not found. """ - node = self.element.find(xpath, self.element.nsmap) # type: ignore + node = self.element.find(xpath, self.element.nsmap) return None if node is None else XmlElement(node) def find_or_throw( @@ -65,10 +64,7 @@ def findall(self, xpath: str) -> List["XmlElement"]: Returns: list[XmlElement]: The found elements. """ - return [ - XmlElement(e) - for e in self.element.findall(xpath, self.element.nsmap) # type: ignore - ] + return [XmlElement(e) for e in self.element.findall(xpath, self.element.nsmap)] @lru_cache(maxsize=100) def find_text(self, xpath: str) -> Optional[str]: