diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc94458..0115b88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,20 +13,83 @@ concurrency: cancel-in-progress: true jobs: + # -------------------------------------------------------------------------- + # LINT (Ruff) + # -------------------------------------------------------------------------- + lint: + name: Lint (Ruff) + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + + - name: Install dependencies + run: | + poetry install --no-interaction --no-ansi --with dev + + - name: Run Ruff + run: | + poetry run ruff format --check . + poetry run ruff check . + + # -------------------------------------------------------------------------- + # TYPE CHECK (Mypy) + # -------------------------------------------------------------------------- + typecheck: + name: Type Check (Mypy) + runs-on: ubuntu-latest + needs: lint + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: true + virtualenvs-in-project: true + installer-parallel: true + + - name: Install dependencies + run: | + poetry install --no-interaction --no-ansi --with dev + + - name: Run Mypy + run: | + poetry run mypy --install-types --non-interactive || true + poetry run mypy src/ tests/ --ignore-missing-imports --disable-error-code=call-overload + + # -------------------------------------------------------------------------- + # TESTS (Pytest + Coverage) + # -------------------------------------------------------------------------- test: - name: Lint • Type Check • Test • Coverage (Python ${{ matrix.python-version }}) + name: Test & Coverage (Pytest) runs-on: ubuntu-latest + needs: typecheck + strategy: fail-fast: false matrix: - include: - - python-version: "3.11" - experimental: false - - python-version: "3.12" - experimental: false - - python-version: "3.13" - experimental: false - continue-on-error: ${{ matrix.experimental }} + python-version: ["3.11", "3.12", "3.13"] steps: - name: Checkout @@ -52,7 +115,7 @@ jobs: path: .venv key: v1-poetry-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }} - - name: Install dependencies (with dev group) + - name: Install dependencies (with dev) if: steps.cache.outputs.cache-hit != 'true' run: | poetry env use "${{ steps.py.outputs.python-path }}" @@ -64,29 +127,36 @@ jobs: poetry env use "${{ steps.py.outputs.python-path }}" poetry install --no-interaction --no-ansi --with dev - - name: Show tool versions + - name: Run Pytest run: | - poetry run python -V - poetry run ruff --version - poetry run mypy --version - poetry run pytest --version + poetry run pytest -q --cov=change_me --cov-report=term-missing --cov-report=xml --cov-fail-under=85 - - name: Ruff (format & lint) - run: | - poetry run ruff format --check . - poetry run ruff check . + - name: Upload coverage XML artifact + if: ${{ matrix.python-version == '3.12' }} + uses: actions/upload-artifact@v4 + with: + name: coverage-xml + path: coverage.xml - - name: Mypy (type check) - run: | - poetry run mypy --install-types --non-interactive || true - poetry run mypy src/ tests/ --ignore-missing-imports --disable-error-code=call-overload + # -------------------------------------------------------------------------- + # COVERALLS (upload coverage) + # -------------------------------------------------------------------------- + coveralls: + name: Coverage Upload (Coveralls) + runs-on: ubuntu-latest + needs: test - - name: Pytest (with coverage) - run: | - poetry run pytest -q --cov=change_me --cov-report=term-missing --cov-report=xml --cov-fail-under=85 + steps: + - name: Checkout + uses: actions/checkout@v4 - - name: Upload coverage to Coveralls - if: ${{ matrix.python-version == '3.12' }} + - name: Download coverage artifact + uses: actions/download-artifact@v4 + with: + name: coverage-xml + path: . + + - name: Upload to Coveralls uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d402749..18991f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,13 +27,16 @@ We welcome issues, bug reports, feature requests, and pull requests. * Follow the existing code style in the project. * Use descriptive names for variables, functions, and classes. * Add docstrings or comments where needed for clarity. + ```bash + make format # easy command for quick formatting help + ``` ### Testing * Ensure that your code runs without errors. * Add or update tests if applicable. * Run all tests before submitting: ```bash - make test + make test # easy command to run all the tests ``` ### Pull Requests diff --git a/Makefile b/Makefile index 2b74d48..8c8c211 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ SHELL := /bin/bash init: python3 -m venv .venv - poetry install + poetry install --with dev pre-commit install poetry env info @echo "Created virtual environment" @@ -30,7 +30,6 @@ update: poetry cache clear pypi --all poetry update - docker: docker build --no-cache -f Dockerfile -t change_me-smoke . docker run --rm change_me-smoke diff --git a/README.md b/README.md index fa43a1f..98f66be 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ Simple README.md for a Python project template. ## Install To install the library run: -```pip install change-me``` +```pip install ``` OR -```pip install git+https://github.com/TUM-Aries-Lab/template-python.git@``` +```pip install git+https://github.com/TUM-Aries-Lab/.git@``` ## Development 0. Install [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) @@ -19,7 +19,14 @@ OR 2. `make format` to format the code and check for errors 3. `make test` to run the test suite 4. `make clean` to delete the temporary files and directories -5. `poetry publish --build` to build and publish to https://pypi.org/project/change-me + +## Publishing +It's super easy to publish your own packages on PyPI. To build and publish this package run: + +```bash +poetry publish --build +``` +The package can then be found at: https://pypi.org/project/change-me ## Module Usage ``` diff --git a/poetry.lock b/poetry.lock index e55c600..7832ea9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,7 +6,7 @@ version = "2025.10.5" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, @@ -18,7 +18,7 @@ version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -30,7 +30,7 @@ version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, @@ -166,7 +166,7 @@ version = "7.11.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "coverage-7.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb53f1e8adeeb2e78962bade0c08bfdc461853c7969706ed901821e009b35e31"}, {file = "coverage-7.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9a03ec6cb9f40a5c360f138b88266fd8f58408d71e89f536b4f91d85721d075"}, @@ -271,7 +271,7 @@ version = "4.0.1" description = "Show coverage stats online via coveralls.io" optional = false python-versions = "<3.13,>=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "coveralls-4.0.1-py3-none-any.whl", hash = "sha256:7a6b1fa9848332c7b2221afb20f3df90272ac0167060f41b5fe90429b30b1809"}, {file = "coveralls-4.0.1.tar.gz", hash = "sha256:7b2a0a2bcef94f295e3cf28dcc55ca40b71c77d1c2446b538e85f0f7bc21aa69"}, @@ -291,7 +291,7 @@ version = "0.4.0" description = "Distribution utilities" optional = false python-versions = "*" -groups = ["main"] +groups = ["dev"] files = [ {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"}, {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"}, @@ -303,7 +303,7 @@ version = "0.6.2" description = "Pythonic argument parser, that will make you smile" optional = false python-versions = "*" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] @@ -314,7 +314,7 @@ version = "3.20.0" description = "A platform independent file lock." optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["dev"] files = [ {file = "filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2"}, {file = "filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4"}, @@ -326,7 +326,7 @@ version = "2.6.15" description = "File identification library for Python" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["dev"] files = [ {file = "identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757"}, {file = "identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf"}, @@ -341,7 +341,7 @@ version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, @@ -356,7 +356,7 @@ version = "2.3.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.10" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, @@ -459,7 +459,7 @@ version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] +groups = ["dev"] files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, @@ -555,7 +555,7 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -579,7 +579,7 @@ version = "4.5.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.10" -groups = ["main"] +groups = ["dev"] files = [ {file = "platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3"}, {file = "platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312"}, @@ -596,7 +596,7 @@ version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -612,7 +612,7 @@ version = "2.2.1" description = "Poetry PEP 517 Build Backend" optional = false python-versions = "<4.0,>=3.9" -groups = ["main"] +groups = ["dev"] files = [ {file = "poetry_core-2.2.1-py3-none-any.whl", hash = "sha256:bdfce710edc10bfcf9ab35041605c480829be4ab23f5bc01202cfe5db8f125ab"}, {file = "poetry_core-2.2.1.tar.gz", hash = "sha256:97e50d8593c8729d3f49364b428583e044087ee3def1e010c6496db76bd65ac5"}, @@ -624,7 +624,7 @@ version = "4.3.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["dev"] files = [ {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"}, {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"}, @@ -643,7 +643,7 @@ version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, @@ -658,7 +658,7 @@ version = "8.4.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, @@ -680,7 +680,7 @@ version = "6.3.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "pytest_cov-6.3.0-py3-none-any.whl", hash = "sha256:440db28156d2468cafc0415b4f8e50856a0d11faefa38f30906048fe490f1749"}, {file = "pytest_cov-6.3.0.tar.gz", hash = "sha256:35c580e7800f87ce892e687461166e1ac2bcb8fb9e13aea79032518d6e503ff2"}, @@ -700,7 +700,7 @@ version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, @@ -783,7 +783,7 @@ version = "2.32.5" description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, @@ -845,7 +845,7 @@ version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" -groups = ["main", "dev"] +groups = ["dev"] files = [ {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, @@ -863,7 +863,7 @@ version = "20.35.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b"}, {file = "virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c"}, @@ -897,4 +897,4 @@ dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] [metadata] lock-version = "2.1" python-versions = "^3.11, <3.13" -content-hash = "18418eb138c9e3337d5bd73e57045fe452a315759beb70fd504b74c89f4752e2" +content-hash = "848436cfa4439431639ae0c264c3b7b8bafca35d2081f68d337d6cfa4d86261d" diff --git a/pyproject.toml b/pyproject.toml index ac6e515..b0708dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,18 +9,15 @@ package-mode = false [tool.poetry.dependencies] python = "^3.11, <3.13" numpy = "^2.2.3" -pytest = "^8.3.5" -pytest-cov = "^6.0.0" loguru = "^0.7.3" -pre-commit = "^4.1.0" -poetry-core = "^2.1.2" -coveralls = "^4.0.1" [tool.poetry.group.dev.dependencies] ruff = "^0.6.9" mypy = "^1.11" pytest = "^8.3" pytest-cov = "^6.0" +pre-commit = "^4.1.0" +poetry-core = "^2.1.2" coveralls = "^4.0.1" [build-system] @@ -74,7 +71,7 @@ lint.ignore = [ fix = true # Sort imports automatically -[tool.lint.isort] +[tool.ruff.lint.isort] known-first-party = ["change-me"] # Replace it with your project name combine-as-imports = true