diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index b172375f..00000000 --- a/.coveragerc +++ /dev/null @@ -1,5 +0,0 @@ -[paths] -source = - src - /workspace/src - **/lib/python*/site-packages diff --git a/.github/workflows/check_pyproject.yaml b/.github/workflows/check_pyproject.yaml new file mode 100644 index 00000000..d929c799 --- /dev/null +++ b/.github/workflows/check_pyproject.yaml @@ -0,0 +1,16 @@ +name: Check if the config schema and the example are up to date. + +on: push + +jobs: + static-code-analysis: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - id: common + uses: ghga-de/gh-action-common@v4 + + - name: Check pyproject.toml + run: | + ./scripts/update_pyproject.py --check diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index 10e9574c..00000000 --- a/.mypy.ini +++ /dev/null @@ -1,12 +0,0 @@ -# Global options: - -[mypy] -disable_error_code = import -show_error_codes = True -exclude = (?x)( - build/lib/ - ) -warn_redundant_casts = True -warn_unused_ignores = True -check_untyped_defs = True -no_site_packages = False diff --git a/.pyproject_generation/pyproject_custom.toml b/.pyproject_generation/pyproject_custom.toml new file mode 100644 index 00000000..5f8c2276 --- /dev/null +++ b/.pyproject_generation/pyproject_custom.toml @@ -0,0 +1,19 @@ +[project] +# please adapt to package name +name = "my_microservice" +version = "0.1.0" +description = "My-Microservice - a short description" +dependencies = [ + "typer >= 0.9.0", + "ghga-service-commons[api] >= 1.2.0", + "ghga-event-schemas >= 1.0.0", + "hexkit[akafka,s3,mongodb] >= 1.1.0" +] + +[project.urls] +# please adapt to package name +Repository = "https://github.com/ghga-de/my-microservice" + +[project.scripts] +# please adapt to package name +my-microservice = "my_microservice.__main__:run" diff --git a/.ruff.toml b/.pyproject_generation/pyproject_template.toml similarity index 55% rename from .ruff.toml rename to .pyproject_generation/pyproject_template.toml index 75a9dbe4..c6bb5fc0 100644 --- a/.ruff.toml +++ b/.pyproject_generation/pyproject_template.toml @@ -1,3 +1,30 @@ +[build-system] +requires = ["setuptools>=67.7.2"] +build-backend = "setuptools.build_meta" + +[project] +readme = "README.md" +authors = [ + { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" }, +] +requires-python = ">=3.9" +license = { text = "Apache 2.0" } +classifiers = [ + "Development Status :: 1 - Planning", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "License :: OSI Approved :: Apache Software License", + "Topic :: Internet :: WWW/HTTP :: HTTP Servers", + "Topic :: Software Development :: Libraries", + "Intended Audience :: Developers", +] + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.ruff] exclude = [ ".git", ".devcontainer", @@ -5,7 +32,6 @@ exclude = [ "build", "dist", ] - ignore = [ "E", # pycodestyle errors "W", # pycodestyle warnings - pycodestyle covered by black @@ -23,9 +49,7 @@ ignore = [ "D206", # indent-with-spaces (ignored for formatter) "D300", # triple-single-quotes (ignored for formatter) ] - line-length = 88 - select = [ "C90", # McCabe Complexity "F", # pyflakes codes @@ -39,26 +63,45 @@ select = [ "SIM", # flake8-simplify "D", # pydocstyle ] - fixable = [ "UP", # e.g. List -> list "I", # sort imports "D", # pydocstyle ] - src = ["src", "tests", "examples", "scripts"] - target-version = "py39" -[mccabe] +[tool.ruff.mccabe] max-complexity = 10 -[per-file-ignores] +[tool.ruff.per-file-ignores] "scripts/*" = ["PL", "S", "SIM", "D"] "tests/*" = ["S", "SIM", "PLR", "B011"] ".devcontainer/*" = ["S", "SIM", "D"] "examples/*" = ["S", "D"] "__init__.py" = ["D"] -[pydocstyle] +[tool.ruff.pydocstyle] convention = "pep257" + +[tool.mypy] +disable_error_code = "import" +show_error_codes = true +exclude = [ + 'build/lib/', +] +warn_redundant_casts = true +warn_unused_ignores = true +check_untyped_defs = true +no_site_packages = false + +[tool.pytest.ini_options] +minversion = "7.1" +asyncio_mode = "strict" + +[tool.coverage.paths] +source = [ + "src", + "/workspace/src", + "**/lib/python*/site-packages", +] diff --git a/.template/mandatory_files.txt b/.template/mandatory_files.txt index 218a58ab..660a15ed 100644 --- a/.template/mandatory_files.txt +++ b/.template/mandatory_files.txt @@ -16,6 +16,8 @@ scripts/script_utils/fastapi_app_location.py .readme_generation/description.md .readme_generation/design.md +.pyproject_generation/pyproject_custom.toml + lock/requirements-dev.in lock/requirements-dev.txt lock/requirements.txt diff --git a/.template/static_files.txt b/.template/static_files.txt index 65f9e0c5..ade3194f 100644 --- a/.template/static_files.txt +++ b/.template/static_files.txt @@ -26,12 +26,14 @@ scripts/update_openapi_docs.py scripts/update_readme.py scripts/update_lock.py scripts/update_hook_revs.py +scripts/update_pyproject.py scripts/list_outdated_dependencies.py scripts/README.md .github/workflows/check_config_docs.yaml .github/workflows/check_openapi_spec.yaml .github/workflows/check_readme.yaml +./github/workflows/check_pyproject.yaml .github/workflows/check_template_files.yaml .github/workflows/ci_release.yaml .github/workflows/ci_workflow_dispatch.yaml @@ -45,6 +47,9 @@ example_data/README.md .readme_generation/.readme_template.md .readme_generation/README.md +.pyproject_generation/pyproject_template.toml +.pyproject_generation/README.md + lock/requirements-dev-template.in lock/README.md diff --git a/pyproject.toml b/pyproject.toml index 903da80b..c832b51b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,43 +1,133 @@ [build-system] -requires = ["setuptools>=67.7.2"] +requires = [ + "setuptools>=67.7.2", +] build-backend = "setuptools.build_meta" [project] -# please adapt to package name name = "my_microservice" version = "0.1.0" description = "My-Microservice - a short description" -readme = "README.md" -authors = [ - { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" }, -] -requires-python = ">=3.9" -license = { text = "Apache 2.0" } -classifiers = [ - "Development Status :: 1 - Planning", - "Operating System :: POSIX :: Linux", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "License :: OSI Approved :: Apache Software License", - "Topic :: Internet :: WWW/HTTP :: HTTP Servers", - "Topic :: Software Development :: Libraries", - "Intended Audience :: Developers", -] dependencies = [ "typer >= 0.9.0", "ghga-service-commons[api] >= 1.2.0", "ghga-event-schemas >= 1.0.0", - "hexkit[akafka,s3,mongodb] >= 1.1.0" + "hexkit[akafka,s3,mongodb] >= 1.1.0", ] [project.urls] -# please adapt to package name Repository = "https://github.com/ghga-de/my-microservice" [project.scripts] -# please adapt to package name my-microservice = "my_microservice.__main__:run" [tool.setuptools.packages.find] -where = ["src"] +where = [ + "src", +] + +[tool.ruff] +exclude = [ + ".git", + ".devcontainer", + "__pycache__", + "build", + "dist", +] +ignore = [ + "E", + "W", + "PLW", + "RUF001", + "RUF010", + "RUF012", + "N818", + "B008", + "PLR2004", + "D205", + "D400", + "D401", + "D107", + "D206", + "D300", +] +line-length = 88 +select = [ + "C90", + "F", + "I", + "S", + "B", + "N", + "UP", + "PL", + "RUF", + "SIM", + "D", +] +fixable = [ + "UP", + "I", + "D", +] +src = [ + "src", + "tests", + "examples", + "scripts", +] +target-version = "py39" + +[tool.ruff.mccabe] +max-complexity = 10 + +[tool.ruff.per-file-ignores] +"scripts/*" = [ + "PL", + "S", + "SIM", + "D", +] +"tests/*" = [ + "S", + "SIM", + "PLR", + "B011", +] +".devcontainer/*" = [ + "S", + "SIM", + "D", +] +"examples/*" = [ + "S", + "D", +] +"__init__.py" = [ + "D", +] + +[tool.ruff.pydocstyle] +convention = "pep257" + +[tool.mypy] +disable_error_code = "import" +show_error_codes = true +exclude = [ + "build/lib/", +] +warn_redundant_casts = true +warn_unused_ignores = true +check_untyped_defs = true +no_site_packages = false + +[tool.pytest.ini_options] +minversion = "7.1" +asyncio_mode = "strict" + +[tool.coverage.paths] +source = [ + "src", + "/workspace/src", + "**/lib/python*/site-packages", +] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index ed67994e..00000000 --- a/pytest.ini +++ /dev/null @@ -1,5 +0,0 @@ -# pytest.ini - -[pytest] -minversion = 7.1 -asyncio_mode = strict diff --git a/scripts/update_pyproject.py b/scripts/update_pyproject.py new file mode 100755 index 00000000..82aaf60f --- /dev/null +++ b/scripts/update_pyproject.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +# Copyright 2021 - 2023 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln +# for the German Human Genome-Phenome Archive (GHGA) +# +# 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. +# + +"""A script to update the pyproject.toml.""" + +import sys +from pathlib import Path + +import tomli +import tomli_w + +from script_utils import cli + +REPO_ROOT_DIR = Path(__file__).parent.parent.resolve() +PYPROJECT_GENERATION_DIR = REPO_ROOT_DIR / ".pyproject_generation" + +PYPROJECT_TEMPLATE_PATH = PYPROJECT_GENERATION_DIR / "pyproject_template.toml" +PYPROJECT_CUSTOM_PATH = PYPROJECT_GENERATION_DIR / "pyproject_custom.toml" +PYPROJECT_TOML = REPO_ROOT_DIR / "pyproject.toml" + + +def read_template_pyproject() -> dict[str, object]: + """Read the pyproject_template.toml.""" + with open(PYPROJECT_TEMPLATE_PATH, "rb") as file: + return tomli.load(file) + + +def read_custom_pyproject() -> dict[str, object]: + """Read the pyproject_custom.toml.""" + with open(PYPROJECT_CUSTOM_PATH, "rb") as file: + return tomli.load(file) + + +def read_current_pyproject() -> dict[str, object]: + """Read the current pyproject.toml.""" + with open(PYPROJECT_TOML, "rb") as file: + return tomli.load(file) + + +def write_pyproject(pyproject: dict[str, object]) -> None: + """Write the given pyproject dict into the pyproject.toml.""" + with open(PYPROJECT_TOML, "wb") as file: + tomli_w.dump(pyproject, file) + + +def merge_pyprojects(inputs: list[dict[str, object]]) -> dict[str, object]: + """Compile a pyproject dict from the provided input dicts.""" + pyproject = inputs[0] + + for input in inputs[1:]: + pyproject.update(input) + + return pyproject + + +def main(*, check: bool = False): + """Update the pyproject.toml or checks for updates if the check flag is specified.""" + template_pyproject = read_template_pyproject() + custom_pyproject = read_custom_pyproject() + merged_pyproject = merge_pyprojects([template_pyproject, custom_pyproject]) + + if check: + current_pyproject = read_current_pyproject() + + if current_pyproject != merged_pyproject: + cli.echo_failure("The pyproject.toml is not up to date.") + sys.exit(1) + + cli.echo_success("The pyproject.toml is up to date.") + return + + write_pyproject(merged_pyproject) + cli.echo_success("Successfully updated the pyproject.toml.") + + +if __name__ == "__main__": + cli.run(main)