diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..526b252 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "build(deps): " + reviewers: + - "OLILHR" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "build(deps): " + reviewers: + - "OLILHR" \ No newline at end of file diff --git a/.github/workflows/byte_order_marks.yml b/.github/workflows/byte_order_marks.yml new file mode 100644 index 0000000..f344615 --- /dev/null +++ b/.github/workflows/byte_order_marks.yml @@ -0,0 +1,15 @@ +name: "Prevent byte order marks." + +on: + push: + branches: + - main + pull_request: {} + +jobs: + bom-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: arma-actions/bom-check@v1 + name: Check for BOM \ No newline at end of file diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000..777b86d --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,27 @@ +name: "Coverage." + +on: + push: + branches: + - main + pull_request: {} +jobs: + coverage: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + - name: Run tests and determine coverage + run: | + tox -e coverage \ No newline at end of file diff --git a/.github/workflows/dependabot_automerge.yml b/.github/workflows/dependabot_automerge.yml new file mode 100644 index 0000000..1a15719 --- /dev/null +++ b/.github/workflows/dependabot_automerge.yml @@ -0,0 +1,19 @@ +name: "Dependabot auto-approve/-merge." + +on: pull_request + +jobs: + dependabot: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Approve pull request + run: gh pr review --approve "$PR_URL" + - name: Enable auto-merge + run: gh pr merge --auto --squash "$PR_URL" \ No newline at end of file diff --git a/.github/workflows/dev_environment.yml b/.github/workflows/dev_environment.yml new file mode 100644 index 0000000..d3be228 --- /dev/null +++ b/.github/workflows/dev_environment.yml @@ -0,0 +1,27 @@ +name: "Test dev environment." + +on: + push: + branches: + - main + pull_request: {} +jobs: + check: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + - name: Create dev environment + run: | + tox -e dev \ No newline at end of file diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml new file mode 100644 index 0000000..7b81be0 --- /dev/null +++ b/.github/workflows/formatting.yml @@ -0,0 +1,28 @@ +name: "Formatting." + +on: + push: + branches: + - main + pull_request: {} +jobs: + black: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + tool: ["black", "isort"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[formatting] + - name: ${{ matrix.tool }} Code Formatter + run: | + ${{ matrix.tool }} . --check \ No newline at end of file diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..4b52731 --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,29 @@ +name: "Linting." + +on: + push: + branches: + - main + pull_request: {} +jobs: + pylint: + name: Maintain code quality + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + linter-env: ["linting", "typechecking", "spellchecking"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + - name: Run ${{ matrix.linter-env }} via tox + run: | + tox -e ${{ matrix.linter-env }} \ No newline at end of file diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml new file mode 100644 index 0000000..f9690a2 --- /dev/null +++ b/.github/workflows/packaging.yml @@ -0,0 +1,38 @@ +name: "Packaging." + +on: [pull_request] + +jobs: + packaging: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + + - name: Create temporary version tag + run: | + if [ -z "$(git tag)" ]; then + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + git tag -a v0.0.0 -m "temporary tag" + fi + + - name: Run packaging test + run: | + tox -e packaging \ No newline at end of file diff --git a/.github/workflows/publishing.yml b/.github/workflows/publishing.yml new file mode 100644 index 0000000..b354e5d --- /dev/null +++ b/.github/workflows/publishing.yml @@ -0,0 +1,57 @@ +name: "Publishing to PyPI." + +on: + release: + types: [created, edited] + +jobs: + testing: + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install tox + run: | + python -m pip install --upgrade pip + pip install tox + - name: Run tests + run: | + tox + + deploying: + name: Build and deploy package to PyPI + runs-on: ubuntu-latest + # GitHub repository settings/environments/create "release" environment + # set up trusted publishing on https://pypi.org/ @ settings/manage/publishing + environment: release + permissions: + id-token: write + needs: testing + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install .[packaging] + - name: Build wheel and source distributions + run: | + pdm build + - name: Publish to PyPI + if: startsWith(github.ref, 'refs/tags/v') + uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml new file mode 100644 index 0000000..6787bf3 --- /dev/null +++ b/.github/workflows/unittests.yml @@ -0,0 +1,27 @@ +name: "Unittests." + +on: + push: + branches: + - main + pull_request: {} +jobs: + pytest: + runs-on: ${{ matrix.os }} + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox + - name: Run unittests via tox + run: | + tox -e testing \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e5a337 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +.env* +!*.example + +.coverage +.idea/ +.pdm-build/ +.pytest_cache/ +.tox/ +.venv + +__pycache__/ +dist/ +htmlcov/ + +# OS +.DS_Store +Thumbs.db diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000..399c9af --- /dev/null +++ b/pdm.lock @@ -0,0 +1,11 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:7e16fee10ffaf1bc79ab939d193afc7d266702675ac0230074435e77467871da" + +[[metadata.targets]] +requires_python = ">=3.13" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..50c26d3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,93 @@ +[project] +name = "python-tox-template" +description = "codebase consolidation tool" +license = { file = "LICENSE" } +requires-python = ">=3.12" +authors = [{ name = "OLILHR" }] +keywords = [] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Software Development :: Libraries :: Python Modules", +] +dynamic = ["readme", "version"] +dependencies = [] + +[project.optional-dependencies] +coverage = [ + "coverage==7.6.8" +] +dev = [ + "pip-tools" +] +linting = [ + "pylint==3.3.2" +] + +formatting = [ + "black==24.10.0", + "isort==5.13.2" +] +packaging = [ + "pdm==2.21.0", + "twine==6.0.1" +] +spellchecking = [ + "codespell==2.3.0" +] +testing = [ + "pytest==8.3.4" +] +typechecking = [ + "mypy==1.13.0" +] +[project.scripts] + +[project.urls] +Changelog = "https://github.com/OLILHR/python-tox-template/releases" +Homepage = "https://github.com/OLILHR/python-tox-template" + +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + +[tool.pdm.version] +source = "scm" + +[tool.pdm.build] +includes = ["src"] +excludes = ["/unittests"] + +[tool.pdm.readme] +content-type = "text/markdown" +path = "README.md" + +[tool.black] +line-length = 120 +target_version = ["py312", "py313"] + +[tool.isort] +line_length = 120 +profile = "black" + +[mypy] +truethy-bool = true + +[tool.mypy] +disable_error_code = ["no-untyped-def", "no-untyped-call"] + +[tool.pylint.format] +max-line-length = 120 + +[tool.pylint."MESSAGES CONTROL"] +disable = [ + "too-few-public-methods", + "fixme", + ] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fdc6042 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +# +# This file is autogenerated by pip-compile with Python 3.13 +# by the following command: +# +# pip-compile pyproject.toml +# \ No newline at end of file diff --git a/src/package/__init__.py b/src/package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/package/module.py b/src/package/module.py new file mode 100644 index 0000000..7108e95 --- /dev/null +++ b/src/package/module.py @@ -0,0 +1,22 @@ +""" +Docstring for this module. +""" + + +# pylint: disable=too-few-public-methods +class ModuleClass: + """ + Docstring for this class. + """ + + def __init__(self) -> None: + """ + Initialize for the sake of initializing. + """ + self.my_instance_var: str = "python-tox-template" + + def function(self) -> str: + """ + Docstring for this function. + """ + return self.my_instance_var diff --git a/src/package/py.typed b/src/package/py.typed new file mode 100644 index 0000000..0d7bf36 --- /dev/null +++ b/src/package/py.typed @@ -0,0 +1,2 @@ +# This file marks mypackage as PEP561 compatible. +# Further reading: https://mypy.readthedocs.io/en/stable/installed_packages.html#creating-pep-561-compatible-packages \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..22286bd --- /dev/null +++ b/tox.ini @@ -0,0 +1,79 @@ +[tox] +envlist = + coverage + linting + spellchecking + testing + typechecking +skip_missing_interpreters = True +skipsdist = True + +[testenv] +commands = python -m pip install --upgrade pip + +[testenv:coverage] +changedir = unittests +deps = + {[testenv:testing]deps} + .[coverage] +setenv = PYTHONPATH = {toxinidir}/src +commands = + coverage run -m pytest --basetemp={envtmpdir} {posargs} + coverage html --omit .tox/*,unittests/* + coverage report --fail-under 80 --omit .tox/*,unittests/* + +[testenv:linting] +deps = + {[testenv:testing]deps} + .[linting] +setenv = PYTHONPATH = {toxinidir}/src +commands = + pylint package + pylint unittests --rcfile=unittests/.pylintrc + +[testenv:spellchecking] +setenv = PYTHONPATH = {toxinidir}/src +deps = + -r requirements.txt + .[spellchecking] +commands = + codespell src/ + codespell README.md + +[testenv:testing] +deps = + -r requirements.txt + .[testing] +setenv = PYTHONPATH = {toxinidir}/src +commands = python -m pytest -x --basetemp={envtmpdir} {posargs} + +[testenv:typechecking] +setenv = PYTHONPATH = {toxinidir}/src +deps = + {[testenv:testing]deps} + .[typechecking] +commands = + mypy --show-error-codes src/package --strict + mypy --show-error-codes unittests --strict + +[testenv:dev] +deps = + {[testenv:coverage]deps} + {[testenv:linting]deps} + {[testenv:packaging]deps} + {[testenv:spellchecking]deps} + {[testenv:testing]deps} + {[testenv:typechecking]deps} + .[formatting] + .[dev] +commands = + python -m pip install --upgrade pip + pip install -r requirements.txt + +[testenv:packaging] +skip_install = true +deps = + .[packaging] +commands = + pdm build + twine check dist/* diff --git a/unittests/.pylintrc b/unittests/.pylintrc new file mode 100644 index 0000000..cc123a6 --- /dev/null +++ b/unittests/.pylintrc @@ -0,0 +1,11 @@ +[pylint] +# for the unittests disable some pylint warnings, e.g. we don't want to force devs to write docstrings for every test +disable = + C0114, # disable missing-module-docstring + C0115, # disable missing-class-docstring + C0116, # disable missing-function-docstring + R0903, # disable too-few-public-methods + W0621, # disable redefined-outer-name (this is for the use of pytest fixtures) + R0801, # disable duplicate-code (we prefer our test to be explicit rather than perfectly redundancy free) +[pylint."MESSAGES CONTROL"] +max-line-length = 120 \ No newline at end of file diff --git a/unittests/__init__.py b/unittests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unittests/test_module.py b/unittests/test_module.py new file mode 100644 index 0000000..49f5ffd --- /dev/null +++ b/unittests/test_module.py @@ -0,0 +1,11 @@ +from package.module import ModuleClass + + +class TestMyClass: + """ + Docstring for this test. + """ + + def test_something(self) -> None: + module_class = ModuleClass() + assert module_class.function() == "python-tox-template"