From c6c259c072448393b519f97c890fba744dc49206 Mon Sep 17 00:00:00 2001 From: Karel van Klink <karel.vanklink@geant.org> Date: Wed, 26 Mar 2025 18:02:06 +0100 Subject: [PATCH] Add boilerplate for Python project --- .gitignore | 16 +++++- .pre-commit-config.yaml | 12 +++++ Changelog.md | 4 ++ Dockerfile | 30 +++++++++++ LICENSE | 21 ++++++++ pyproject.toml | 117 ++++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 ++ setup.py | 30 +++++++++++ tox.ini | 23 ++++++++ 9 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 .pre-commit-config.yaml create mode 100644 Changelog.md create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 pyproject.toml create mode 100644 setup.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index f7275bb..3752bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,15 @@ -venv/ +__pycache__/ +*.egg-info +.coverage* +coverage.xml +.vscode +venv +oss-params.json +.mypy_cache +.pytest_cache +.ruff_cache +.tox +build/ +.idea +.venv +.env diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a825c2d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.15 + hooks: + # Run the linter. + - id: ruff + args: + - --fix + - --preview + - --ignore=PLR0917,PLR0914 + - --extend-exclude=test/* diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 0000000..ee20d1f --- /dev/null +++ b/Changelog.md @@ -0,0 +1,4 @@ +# Changelog + +## [0.1] - 2025-03-26 +- Initial version diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c17bbc3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.12.7-alpine +WORKDIR /app + +# Set environment variables for predictable Python behavior and UTF-8 encoding +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + LANG=C.UTF-8 \ + LC_ALL=C.UTF-8 + +ARG ARTIFACT_VERSION + +RUN apk add --no-cache gcc libc-dev libffi-dev && \ + addgroup -S appgroup && adduser -S appuser -G appgroup -h /app + +RUN pip install --no-cache-dir \ + --pre \ + --trusted-host 150.254.211.2 \ + --extra-index-url https://150.254.211.2/artifactory/api/pypi/geant-swd-pypi/simple \ + --target /app \ + geant-capacity-planner==${ARTIFACT_VERSION} + +# Copy the shell scripts and ensure scripts do not have Windows line endings and make them executable +COPY start-app.sh start-worker.sh start-scheduler.sh /app/ +RUN sed -i 's/\r$//' start-app.sh start-worker.sh start-scheduler.sh && \ + chmod +x start-app.sh start-worker.sh start-scheduler.sh + +RUN chown -R appuser:appgroup /app +USER appuser +EXPOSE 8080 +ENTRYPOINT ["/app/start-app.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b47daa7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 GÉANT Vereniging + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..542d08a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,117 @@ +[tool.mypy] +exclude = ["venv"] +ignore_missing_imports = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +disallow_untyped_decorators = true +no_implicit_optional = true +strict_optional = true +namespace_packages = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_no_return = true +warn_unreachable = true +implicit_reexport = false +strict_equality = true +show_error_codes = true +show_column_numbers = true +# Suppress "note: By default the bodies of untyped functions are not checked" +disable_error_code = "annotation-unchecked" +# Forbid the use of a generic "type: ignore" without specifying the exact error that is ignored +enable_error_code = "ignore-without-code" + +[tool.ruff] +extend-exclude = [ + "htmlcov", + "docs", +] +target-version = "py312" +line-length = 120 + +[tool.ruff.lint] +ignore = [ + "C901", + "COM812", + "D203", + "D213", + "ISC001", + "N805", + "PLC2801", + "PLR0913", + "PLR0904", + "PLW1514", +] +select = [ + "A", + "ARG", + "B", + "BLE", + "C", + "COM", + "C4", + "C90", + "D", + "DTZ", + "E", + "EM", + "ERA", + "F", + "FA", + "FBT", + "FLY", + "FURB", + "G", + "I", + "ICN", + "INP", + "ISC", + "LOG", + "N", + "PERF", + "PGH", + "PIE", + "PL", + "PT", + "PTH", + "PYI", + "Q", + "RET", + "R", + "RET", + "RSE", + "RUF", + "S", + "SIM", + "SLF", + "T", + "T20", + "TID", + "TRY", + "UP", + "W", + "YTT" +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.flake8-tidy-imports] +ban-relative-imports = "all" + +[tool.ruff.lint.per-file-ignores] +"setup.py" = ["D100"] + +[tool.ruff.lint.isort] +known-third-party = ["pydantic"] +known-first-party = ["test", "docs"] + +[tool.pytest.ini_options] +markers = [ + "noautofixt" +] +filterwarnings = [ + "ignore", + "default:::capacity-planner", +] +asyncio_default_fixture_loop_scope = "function" diff --git a/requirements.txt b/requirements.txt index 1d4f386..d3be924 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,7 @@ six==1.17.0 tabulate==0.9.0 tzdata==2025.1 urllib3==2.3.0 + +# Development requirements +ruff==0.11.2 +mypy==1.15.0 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4933ffc --- /dev/null +++ b/setup.py @@ -0,0 +1,30 @@ +"""Setup script for the GÉANT Capacity Planner.""" + +from setuptools import find_packages, setup + +setup( + name="geant-capacity-planner", + version="0.1", + author="GÉANT Vereniging", + author_email="goat@geant.org", + description="GÉANT Capacity Planner", + url="https://gitlab.software.geant.org/karel.vanklink/daniel", + packages=find_packages(), + install_requires=[ + "certifi==2024.12.14", + "charset-normalizer == 3.4.1", + "idna==3.10", + "networkx==3.4.2", + "numpy==2.2.2", + "pandas==2.2.3", + "python-dateutil==2.9.0.post0", + "pytz==2025.1", + "requests==2.32.3", + "setuptools==75.8.0", + "six==1.17.0", + "tabulate==0.9.0", + "tzdata==2025.1", + "urllib3==2.3.0", + ], + include_package_data=True, +) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..85de47d --- /dev/null +++ b/tox.ini @@ -0,0 +1,23 @@ +[tox] +envlist = py312 + +[pytest] +markers = "workflow,noautofixt" +filterwarnings = "ignore,default:::capacity-planner" + +[testenv] +passenv = SKIP_ALL_TESTS +setenv = + TESTING=true +deps = + pytest-cov + -r requirements.txt + +commands = + ruff check --respect-gitignore --preview . + ruff format --respect-gitignore --preview --check . + mypy . + sh -c 'if [ $SKIP_ALL_TESTS = 1 ]; then echo "Skipping coverage report"; else pytest --cov=gso --cov-report=xml --cov-report=html --cov-fail-under=90 -n auto {posargs}; fi' + +allowlist_externals = + sh -- GitLab