From bf9b8f1d08b0e5cdf08bb5e927b2fc29315f0c20 Mon Sep 17 00:00:00 2001
From: BRAUN REMI <remi.braun@unistra.fr>
Date: Mon, 16 Dec 2024 16:55:36 +0100
Subject: [PATCH] - INTERNAL: Switch from `setup.py` to `pyproject.toml` #109 -
 INTERNAL: Use `ruff` instead of `black` + `flake8` + `isort`

---
 .coveragerc             |  33 -------------
 .gitlab-ci.yml          |  13 ++---
 .pre-commit-config.yaml |  30 +++++-------
 CHANGES.md              |   2 +
 docs/conf.py            |  10 ++--
 eoreader/__init__.py    |  10 +---
 eoreader/__meta__.py    |  13 +----
 pyproject.toml          | 105 ++++++++++++++++++++++++++++++++++++++++
 pytest.ini              |   5 --
 setup.cfg               |  14 ------
 setup.py                |  76 -----------------------------
 tox-conda.ini           |  34 -------------
 tox.ini                 |  34 -------------
 13 files changed, 132 insertions(+), 247 deletions(-)
 delete mode 100644 .coveragerc
 create mode 100644 pyproject.toml
 delete mode 100644 pytest.ini
 delete mode 100644 setup.cfg
 delete mode 100644 setup.py
 delete mode 100644 tox-conda.ini
 delete mode 100644 tox.ini

diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index c3e44a13..00000000
--- a/.coveragerc
+++ /dev/null
@@ -1,33 +0,0 @@
-[run]
-omit =
-    */__init__.py
-
-[report]
-exclude_lines =
-    # Don't complain about missing debug-only code:
-    def __repr__
-    if self\.debug
-
-    # Don't complain if tests don't hit defensive assertion code:
-    raise AssertionError
-    raise NotImplementedError
-    raise InvalidProductError
-    raise InvalidTypeError
-    raise InvalidIndexError
-    raise InvalidBandError
-    raise TypeError
-    raise OsError
-    raise FileNotFoundError
-    raise ValueError
-    raise OSError
-    except
-    LOGGER.warning
-    LOGGER.debug
-    return {}
-
-    # Don't complain if non-runnable code isn't run:
-    if 0:
-    if __name__ == .__main__.:
-
-    # Don't complain about abstract methods, they aren't run:
-    @(abc\.)?abstractmethod
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 93f01e32..701a51d7 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,14 +12,11 @@ lint:
   stage: lint
   script:
     - python -m pip install --upgrade pip
-    - pip install flake8
-    - flake8 eoreader
-  rules:
-    - if: $CI_COMMIT_TAG
-      when: never
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    - when: always
+    - pip install ruff
+    - ruff format
+    - ruff check
+  except:
+    - tags
 
 # Test with data on local disk
 pytest:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f52d14a3..678cdf8a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,35 +1,29 @@
 exclude: |
-  (?x)^(docs/.*)$ |
+  (?x)^(
+      docs/.*
+  )$ |
   .*\.md
 fail_fast: false
 ci:
     autoupdate_schedule: monthly
 repos:
+
   - repo: https://github.com/pre-commit/pre-commit-hooks.git
     rev: v5.0.0
     hooks:
-      - id: trailing-whitespace
-      - id: end-of-file-fixer
       - id: check-json
       - id: check-yaml
         args: [ --allow-multiple-documents, --unsafe ]
       - id: check-xml
       - id: check-added-large-files
         args: [ '--maxkb=1600' ]
-      - id: debug-statements
-      - id: check-merge-conflict
-
-  - repo: 'https://github.com/PyCQA/flake8'
-    rev: 7.1.1
-    hooks:
-      - id: flake8
-
-  - repo: 'https://github.com/psf/black'
-    rev: 24.10.0
-    hooks:
-      - id: black
 
-  - repo: https://github.com/pycqa/isort
-    rev: 5.13.2
+  - repo: https://github.com/astral-sh/ruff-pre-commit
+    # Ruff version.
+    rev: v0.8.3
     hooks:
-      - id: isort
+      # Run the linter.
+      - id: ruff
+        args: [ --fix ]
+      # Run the formatter.
+      - id: ruff-format
diff --git a/CHANGES.md b/CHANGES.md
index 1f456d20..f8fd43b1 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -20,6 +20,8 @@
 - FIX: Fix the computation of parametric spectral indices [#193](https://github.com/sertit/eoreader/issues/193)
 - OPTIM: Cache the access to any archived file list, as this operation is expensive when done with large archives stored on the cloud (and thus better done only once).
 - CI: Remove useless verbosity in CI
+- INTERNAL: Switch from `setup.py` to `pyproject.toml` [#109](https://github.com/sertit/eoreader/issues/109)
+- INTERNAL: Use `ruff` instead of `black` + `flake8` + `isort`
 - DOC: Update `conf.py` (remove useless hunks and set Sphinx 7 as base)
 - DOC: Added the [PAZ product guide](https://earth.esa.int/eogateway/documents/20142/37627/PAZ-Image-Products-Guide.pdf) to the PAZ Product documentation instead of the TerraSAR-X one - by @guillemc23
 - DEPS: Pin `sertit>=1.44.1`
diff --git a/docs/conf.py b/docs/conf.py
index 503c165f..90297c0a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -100,9 +100,9 @@
 master_doc = "index"
 
 # General information about the project.
-project = eoreader.__title__
-copyright = eoreader.__copyright__[10:]
-author = eoreader.__author__
+project = "eoreader"
+copyright = "Copyright 2024, SERTIT-ICube - France, https://sertit.unistra.fr/"
+author = "ICube-SERTIT"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -147,11 +147,11 @@
 # further.  For a list of options available for each theme, see the
 # documentation.
 html_theme_options = {
-    "repository_url": eoreader.__url__,
+    "repository_url": "https://eoreader.readthedocs.io",
     "use_repository_button": True,
     "use_issues_button": True,
     "use_edit_page_button": False,
-    "repository_branch": "master",
+    "repository_branch": "main",
     "path_to_docs": "docs",
     "use_download_button": False,
 }
diff --git a/eoreader/__init__.py b/eoreader/__init__.py
index 59ec78ea..c937d654 100644
--- a/eoreader/__init__.py
+++ b/eoreader/__init__.py
@@ -17,6 +17,7 @@
 """
 **EOReader** library
 """
+
 # flake8: noqa
 from functools import wraps
 from typing import Callable
@@ -44,14 +45,7 @@ def wrapper(*args, **kwargs):
     )
     from functools import cache
 
+
 from .__meta__ import (
-    __author__,
-    __author_email__,
-    __copyright__,
-    __description__,
-    __documentation__,
-    __license__,
-    __title__,
-    __url__,
     __version__,
 )
diff --git a/eoreader/__meta__.py b/eoreader/__meta__.py
index ee4620dd..67164af1 100644
--- a/eoreader/__meta__.py
+++ b/eoreader/__meta__.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 # Copyright 2024, SERTIT-ICube - France, https://sertit.unistra.fr/
 # This file is part of eoreader project
 #     https://github.com/sertit/eoreader
@@ -17,15 +16,5 @@
 """
 **EOReader** library
 """
+
 __version__ = "0.21.7"
-__title__ = "eoreader"
-__description__ = (
-    "Remote-sensing opensource python library reading optical and SAR constellations, "
-    "loading and stacking bands, clouds, DEM and spectral indices in a sensor-agnostic way."
-)
-__author__ = "ICube-SERTIT"
-__author_email__ = "dev-sertit@unistra.fr"
-__url__ = "https://github.com/sertit/eoreader"
-__license__ = "Apache 2.0"
-__copyright__ = "Copyright 2024, SERTIT-ICube - France, https://sertit.unistra.fr/"
-__documentation__ = "https://eoreader.readthedocs.io"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..1d0d6c63
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,105 @@
+[build-system]
+requires = ["setuptools", "setuptools-scm"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools.packages.find]
+include = ["eoreader*"]
+namespaces = false
+
+[project]
+name = "eoreader"
+authors = [
+    {name = "ICube-SERTIT", email = "dev-sertit@unistra.fr"},
+]
+description = "Remote-sensing opensource python library reading optical and SAR constellations, loading and stacking bands, clouds, DEM and spectral indices in a sensor-agnostic way."
+readme = "README.md"
+requires-python = ">=3.9"
+license = {text = "Apache 2.0"}
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Developers",
+    "Intended Audience :: Science/Research",
+    "Natural Language :: English",
+    "License :: OSI Approved :: Apache Software License",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Operating System :: OS Independent",
+    "Topic :: Scientific/Engineering :: GIS",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+]
+dependencies = [
+    "lxml",
+    "h5netcdf",
+    "scipy",
+    "rasterio>=1.3.10",  # numpy >= 2
+    "xarray>=2024.06.0",  # numpy >= 2
+    "rioxarray>=0.10.0",
+    "odc-geo>=0.4.6",
+    "geopandas>=0.14.4",
+    "sertit[full]>=1.44.1",
+    "spyndex>=0.3.0",
+    "pyresample",
+    "zarr",
+    "rtree",
+    "cloudpathlib[s3]>=0.12.1",
+    "validators",
+    "methodtools",
+    "dicttoxml",
+]
+
+[project.optional-dependencies]
+"stac" = [
+    "pystac[validation]",
+    "stac-asset",
+    "planetary_computer",
+]
+[tool.setuptools.package-data]
+eoreader.data = ["*.xml"]
+
+dynamic = ["version"]
+
+[tool.setuptools.dynamic]
+version = {attr = "eoreader.__version__"}
+
+[project.urls]
+Bug_Tracker = "https://github.com/sertit/eoreader/issues"
+Documentation =  "https://eoreader.readthedocs.io/en/latest/"
+Source_Code = "https://github.com/sertit/eoreader"
+
+[tool.ruff.lint]
+select = [
+    # pycodestyle
+    "E",
+    # Pyflakes
+    "F",
+    # pyupgrade
+    "UP",
+    # flake8-bugbear
+    "B",
+    # flake8-simplify
+    "SIM",
+    # isort
+    "I",
+]
+ignore = ["E501"]
+
+[tool.ruff.lint.pyupgrade]
+# Preserve types, even if a file imports `from __future__ import annotations`.
+keep-runtime-typing = true
+
+# Pytest options
+[tool.pytest.ini_options]
+log_cli = true
+log_cli_format = "%(name)s: %(asctime)s - [%(levelname)s] - %(message)s"
+log_cli_date_format = "%Y-%m-%d %H:%M:%S"
+log_cli_level = "INFO"
+
+[tool.coverage.run]
+omit = ["*/__init__.py"]
+
+
+[tool.typos]
+files.extend-exclude = ["*.ipynb", "*.html", ".git/*"]
+default.extend-ignore-re = ["_.*_", "\"\\w*\"", "PN", ", fo", "FRE", "THR", "T60HTE", "_BA"]
\ No newline at end of file
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index c00f69c4..00000000
--- a/pytest.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pytest]
-log_cli = True
-log_cli_format = %(name)s: %(asctime)s - [%(levelname)s] - %(message)s
-log_cli_date_format = %Y-%m-%d %H:%M:%S
-log_cli_level = INFO
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 3d958c37..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,14 +0,0 @@
-[flake8]
-ignore = W605, W503, F405, E501, F403
-max-line-length = 200
-
-[isort]
-multi_line_output = 3
-include_trailing_comma = True
-force_grid_wrap = 0
-use_parentheses = True
-line_length = 88
-ensure_newline_before_comments = True
-known_first_party = eoreader, CI
-profile = black
-skip_glob = */eoreader/products/__init__.py
diff --git a/setup.py b/setup.py
deleted file mode 100644
index d18919c4..00000000
--- a/setup.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import os
-
-import setuptools
-
-from eoreader.__meta__ import (
-    __author__,
-    __author_email__,
-    __description__,
-    __documentation__,
-    __title__,
-    __url__,
-    __version__,
-)
-
-BASEDIR = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
-with open(os.path.join(BASEDIR, "README.md"), "r", encoding="utf8") as f:
-    readme = f.read()
-
-setuptools.setup(
-    name=__title__,
-    version=__version__,
-    author=__author__,
-    author_email=__author_email__,
-    description=__description__,
-    long_description=readme,
-    long_description_content_type="text/markdown",
-    packages=setuptools.find_packages(),
-    install_requires=[
-        "lxml",
-        "h5netcdf",
-        "scipy",
-        "rasterio>=1.3.10",  # numpy >= 2
-        "xarray>=2024.06.0",  # numpy >= 2
-        "rioxarray>=0.10.0",
-        "odc-geo>=0.4.6",
-        "geopandas>=0.14.4",
-        "sertit[full]>=1.44.1",
-        "spyndex>=0.3.0",
-        "pyresample",
-        "zarr",
-        "rtree",
-        "cloudpathlib[s3]>=0.12.1",
-        "validators",
-        "methodtools",
-        "dicttoxml",
-    ],
-    extras_require={
-        "stac": [
-            "pystac[validation]",
-            "stac-asset",
-            "planetary_computer",
-        ]
-    },
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        "Intended Audience :: Developers",
-        "Intended Audience :: Science/Research",
-        "Natural Language :: English",
-        "License :: OSI Approved :: Apache Software License",
-        "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.9",
-        "Programming Language :: Python :: 3.10",
-        "Programming Language :: Python :: 3.11",
-        "Operating System :: OS Independent",
-        "Topic :: Scientific/Engineering :: GIS",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-    ],
-    package_data={"": ["LICENSE", "NOTICE"], "eoreader.data": ["*.xml"]},
-    include_package_data=True,
-    python_requires=">=3.9",
-    project_urls={
-        "Bug Tracker": f"{__url__}/issues/",
-        "Documentation": __documentation__,
-        "Source Code": __url__,
-    },
-)
diff --git a/tox-conda.ini b/tox-conda.ini
deleted file mode 100644
index d24966a9..00000000
--- a/tox-conda.ini
+++ /dev/null
@@ -1,34 +0,0 @@
-[tox]
-envlist = py39, py310, py311, linters
-requires = tox-conda
-
-[gh-actions]
-python =
-    3.11: py311
-    3.10: py310
-    3.9: py39
-
-[testenv]
-commands = pytest -v --durations=0 --cov-report term --cov-report xml:{toxinidir}{/}cov.xml --cov={toxinidir}{/}eoreader CI{/}SCRIPTS
-deps =
-    tox-conda
-    -r{toxinidir}{/}requirements.txt
-
-allowlist_externals = mkdir
-
-setenv =
-    TMPDIR={envtmpdir}
-    HOME={envtmpdir}
-    PIP_EXTRA_INDEX_URL={env:WINDOWS_WHEELS}
-
-passenv =
-    S3_DB_URL_ROOT
-    AWS_ACCESS_KEY_ID
-    AWS_S3_ENDPOINT
-    AWS_SECRET_ACCESS_KEY
-
-[testenv:linters]
-skip_install = true
-basepython = python3
-deps = pre-commit
-commands = pre-commit run --all-files
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 1e794867..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,34 +0,0 @@
-[tox]
-envlist = py39, py310, py311, linters
-skipdist = true
-
-[gh-actions]
-python =
-    3.11: py311
-    3.10: py310
-    3.9: py39
-
-[testenv]
-commands = pytest -v --durations=0 --cov-report term --cov-report xml:{toxinidir}{/}cov.xml --cov={toxinidir}{/}eoreader CI{/}SCRIPTS
-deps =
-    -r{toxinidir}{/}requirements.txt
-
-allowlist_externals = mkdir
-
-setenv =
-    TMPDIR={envtmpdir}
-    HOME={envtmpdir}
-    CPLUS_INCLUDE_PATH = "/usr/include/gdal"
-    C_INCLUDE_PATH = "/usr/include/gdal"
-
-passenv =
-    S3_DB_URL_ROOT
-    AWS_ACCESS_KEY_ID
-    AWS_S3_ENDPOINT
-    AWS_SECRET_ACCESS_KEY
-
-[testenv:linters]
-skip_install = true
-basepython = python3
-deps = pre-commit
-commands = pre-commit run --all-files