From 24476226eaf22a4a30a8c777e446bca6fdc6edb2 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 2 Aug 2024 22:28:14 -0400 Subject: [PATCH 01/28] Remove pins from dev requirements Turns out this is just a pain in the ass to keep updated as requirement constraints grow and adds needless diffs to PRs. --- noxfile.py | 2 - requirements/requirements-dev.in | 16 ------- requirements/requirements-dev.txt | 69 ++++-------------------------- requirements/requirements-test.in | 14 ------ requirements/requirements-test.txt | 42 +++--------------- 5 files changed, 14 insertions(+), 129 deletions(-) delete mode 100644 requirements/requirements-dev.in delete mode 100644 requirements/requirements-test.in diff --git a/noxfile.py b/noxfile.py index 1839451..a64f157 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,8 +16,6 @@ VENV_PATH = "venv" REQUIREMENT_IN_FILES = [ pathlib.Path("requirements/requirements.in"), - pathlib.Path("requirements/requirements-dev.in"), - pathlib.Path("requirements/requirements-test.in"), ] # What we allowed to clean (delete) diff --git a/requirements/requirements-dev.in b/requirements/requirements-dev.in deleted file mode 100644 index 8e39dbc..0000000 --- a/requirements/requirements-dev.in +++ /dev/null @@ -1,16 +0,0 @@ -# Development Requirements - linting, formatting, etc. -# Ensure to set PIP_INDEX_URL to the correct value for your environment -# This is the URL to the Artifactory instance that hosts the Python packages (default: pypi.org) -# This will not be emitted to the requirements*.txt files and must be set in the environment -# before running pip install - -# Constrain versions installed to be compatible with core dependencies ---constraint requirements.txt - -pre-commit -black -mypy -flake8 -flake8-builtins -flake8-pep585 -pep8-naming diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 860df5c..a764745 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,62 +1,9 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --no-emit-index-url requirements/requirements-dev.in -# -black==24.2.0 - # via -r requirements/requirements-dev.in -cfgv==3.4.0 - # via pre-commit -click==8.1.7 - # via black -distlib==0.3.8 - # via virtualenv -filelock==3.13.1 - # via virtualenv -flake8==7.0.0 - # via - # -r requirements/requirements-dev.in - # flake8-builtins - # pep8-naming -flake8-builtins==2.2.0 - # via -r requirements/requirements-dev.in -flake8-pep585==0.1.7 - # via -r requirements/requirements-dev.in -identify==2.5.35 - # via pre-commit -mccabe==0.7.0 - # via flake8 -mypy==1.9.0 - # via -r requirements/requirements-dev.in -mypy-extensions==1.0.0 - # via - # black - # mypy -nodeenv==1.8.0 - # via pre-commit -packaging==24.0 - # via black -pathspec==0.12.1 - # via black -pep8-naming==0.14.1 - # via -r requirements/requirements-dev.in -platformdirs==4.2.0 - # via - # black - # virtualenv -pre-commit==3.6.2 - # via -r requirements/requirements-dev.in -pycodestyle==2.11.1 - # via flake8 -pyflakes==3.2.0 - # via flake8 -pyyaml==6.0.1 - # via pre-commit -typing-extensions==4.10.0 - # via mypy -virtualenv==20.25.1 - # via pre-commit +# Development Requirements - linting, formatting, etc. -# The following packages are considered to be unsafe in a requirements file: -# setuptools +pre-commit +black +mypy +flake8 +flake8-builtins +flake8-pep585 +pep8-naming diff --git a/requirements/requirements-test.in b/requirements/requirements-test.in deleted file mode 100644 index bb87f28..0000000 --- a/requirements/requirements-test.in +++ /dev/null @@ -1,14 +0,0 @@ -# Testing Requirements -# ---------------------------------------- -# Ensure to set PIP_INDEX_URL to the correct value for your environment -# This is the URL to the Artifactory instance that hosts the Python packages (default: pypi.org) -# This will not be emitted to the requirements*.txt files and must be set in the environment -# before running pip install - -# Constrain versions installed to be compatible with core dependencies ---constraint requirements.txt - -pytest -pytest-randomly -coverage -nox diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index d5b288e..e6dafae 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,36 +1,6 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --no-emit-index-url requirements/requirements-test.in -# -argcomplete==3.2.3 - # via nox -colorlog==6.8.2 - # via nox -coverage==7.4.3 - # via -r requirements/requirements-test.in -distlib==0.3.8 - # via virtualenv -filelock==3.13.1 - # via virtualenv -iniconfig==2.0.0 - # via pytest -nox==2024.3.2 - # via -r requirements/requirements-test.in -packaging==24.0 - # via - # nox - # pytest -platformdirs==4.2.0 - # via virtualenv -pluggy==1.4.0 - # via pytest -pytest==8.1.1 - # via - # -r requirements/requirements-test.in - # pytest-randomly -pytest-randomly==3.15.0 - # via -r requirements/requirements-test.in -virtualenv==20.25.1 - # via nox +# Testing Requirements + +pytest +pytest-randomly +coverage +nox From e74dcc2508c9739b137bdb34c0526cca8c376ce4 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 2 Aug 2024 23:21:49 -0400 Subject: [PATCH 02/28] Move contributing details It is as though there was a file for these details all along. --- CONTRIBUTING.md | 169 +++++++++++++++++++++++++++++++++++++++++++---- README.md | 151 ------------------------------------------ init_template.py | 16 +++++ 3 files changed, 171 insertions(+), 165 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb09367..b3083c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,24 +4,15 @@ Before contributing, please either ask to claim an existing open issue or create a new issue to discuss your proposed changes with the owner(s) of this repo before making any changes. -*Any pull requests without an approved issue associated with them will be -closed* +Any pull requests without a clearly defined issue being solved will be closed. -## Bug reports +### Bug reports Found a bug but do not have time or do not wish to contribute a fix? Please submit an issue for our awareness. Your feedback drives the continued development of the project! -## Fork - -Create your own fork of this repo that you will make your changes on. - -## Creating your feature - -Always base your changes off the `main` branch unless otherwise asked. - -## Pull Request +### Pull Request All pull requests must: @@ -30,16 +21,166 @@ All pull requests must: - If the PR is a bug fix there must be a test that duplicates the bug, proving it is fixed -## Code Style +### Code Style Follow the patterns seen in the code. Walk where others have walked. The majority of code style nits will be met when passing `pre-commit` checks prior to submitting a pull request. -## Tests +### Tests - Smaller tests are easier to work with - Mock at a minimum - No test should be dependent on another - No test should be dependent on secrets/tokens + + +--- + +# Local developer installation + +The following steps outline how to install this repo for local development. + +## Prerequisites + +### Clone repo + +```console +git clone https://github.com/[ORG NAME]/[REPO NAME] + +cd [REPO NAME] +``` + +### Virtual Environment + +Use a ([`venv`](https://docs.python.org/3/library/venv.html)), or equivalent, +when working with python projects. Leveraging a `venv` will ensure the installed +dependency files will not impact other python projects or any system +dependencies. + +**Windows users**: Depending on your python install you will use `py` in place +of `python` to create the `venv`. + +**Linux/Mac users**: Replace `python`, if needed, with the appropriate call to +the desired version while creating the `venv`. (e.g. `python3` or `python3.12`) + +**All users**: Once inside an active `venv` all systems should allow the use of +`python` for command line instructions. This will ensure you are using the +`venv`'s python and not the system level python. + +### Create the `venv`: + +```console +python -m venv venv +``` + +Activate the `venv`: + +```console +# Linux/Mac +. venv/bin/activate + +# Windows +venv\Scripts\activate +``` + +The command prompt should now have a `(venv)` prefix on it. `python` will now +call the version of the interpreter used to create the `venv` + +To deactivate (exit) the `venv`: + +```console +deactivate +``` + +--- + +## Developer Installation Steps + +### Install editable library and development requirements + +```console +python -m pip install --editable .[dev,test] +``` + +### Install pre-commit [(see below for details)](#pre-commit) + +```console +pre-commit install +``` + +### Install with nox + +If you have `nox` installed with `pipx` or in the current venv you can use the +following session. This is an alternative to the two steps above. + +```console +nox -s install +``` + +--- + +## Pre-commit and nox tools + +### Run pre-commit on all files + +```console +pre-commit run --all-files +``` + +### Run tests with coverage (quick) + +```console +nox -e coverage +``` + +### Run tests (slow) + +```console +nox +``` + +### Build dist + +```console +nox -e build +``` + +--- + +## Updating dependencies + +New dependencys can be added to the `requirements-*.in` file. It is recommended +to only use pins when specific versions or upgrades beyond a certain version are +to be avoided. Otherwise, allow `pip-compile` to manage the pins in the +generated `requirements-*.txt` files. + +Once updated following the steps below, the package can be installed if needed. + +### Update the generated files with changes + +```console +nox -e update +``` + +### Upgrade all generated dependencies + +```console +nox -e upgrade +``` + +--- + +## [pre-commit](https://pre-commit.com) + +> A framework for managing and maintaining multi-language pre-commit hooks. + +This repo is setup with a `.pre-commit-config.yaml` with the expectation that +any code submitted for review already passes all selected pre-commit checks. + +--- + +## Error: File "setup.py" not found + +Update `pip` to at least version 22.3.1 diff --git a/README.md b/README.md index 8861f43..40312e7 100644 --- a/README.md +++ b/README.md @@ -37,154 +37,3 @@ fit. - **A:** I'm constantly finding new tweaks that make the template fit just a little better. I'm also open to ideas and suggestions so please drop an issue if you have one. - ---- - -# Local developer installation - -The following steps outline how to install this repo for local development. See -the [CONTRIBUTING.md](CONTRIBUTING.md) file in the repo root for information on -contributing to the repo. - -## Prerequisites - -### Clone repo - -```console -git clone https://github.com/[ORG NAME]/[REPO NAME] - -cd [REPO NAME] -``` - -### Virtual Environment - -Use a ([`venv`](https://docs.python.org/3/library/venv.html)), or equivalent, -when working with python projects. Leveraging a `venv` will ensure the installed -dependency files will not impact other python projects or any system -dependencies. - -**Windows users**: Depending on your python install you will use `py` in place -of `python` to create the `venv`. - -**Linux/Mac users**: Replace `python`, if needed, with the appropriate call to -the desired version while creating the `venv`. (e.g. `python3` or `python3.8`) - -**All users**: Once inside an active `venv` all systems should allow the use of -`python` for command line instructions. This will ensure you are using the -`venv`'s python and not the system level python. - -### Create the `venv`: - -```console -python -m venv venv -``` - -Activate the `venv`: - -```console -# Linux/Mac -. venv/bin/activate - -# Windows -venv\Scripts\activate -``` - -The command prompt should now have a `(venv)` prefix on it. `python` will now -call the version of the interpreter used to create the `venv` - -To deactivate (exit) the `venv`: - -```console -deactivate -``` - ---- - -## Developer Installation Steps - -### Install editable library and development requirements - -```console -python -m pip install --editable .[dev,test] -``` - -### Install pre-commit [(see below for details)](#pre-commit) - -```console -pre-commit install -``` - -### Install with nox - -If you have `nox` installed with `pipx` or in the current venv you can use the -following session. This is an alternative to the two steps above. - -```console -nox -s install -``` - ---- - -## Pre-commit and nox tools - -### Run pre-commit on all files - -```console -pre-commit run --all-files -``` - -### Run tests with coverage (quick) - -```console -nox -e coverage -``` - -### Run tests (slow) - -```console -nox -``` - -### Build dist - -```console -nox -e build -``` - ---- - -## Updating dependencies - -New dependencys can be added to the `requirements-*.in` file. It is recommended -to only use pins when specific versions or upgrades beyond a certain version are -to be avoided. Otherwise, allow `pip-compile` to manage the pins in the -generated `requirements-*.txt` files. - -Once updated following the steps below, the package can be installed if needed. - -### Update the generated files with changes - -```console -nox -e update -``` - -### Upgrade all generated dependencies - -```console -nox -e upgrade -``` - ---- - -## [pre-commit](https://pre-commit.com) - -> A framework for managing and maintaining multi-language pre-commit hooks. - -This repo is setup with a `.pre-commit-config.yaml` with the expectation that -any code submitted for review already passes all selected pre-commit checks. - ---- - -## Error: File "setup.py" not found - -Update `pip` to at least version 22.3.1 diff --git a/init_template.py b/init_template.py index 2dcd787..86252d5 100644 --- a/init_template.py +++ b/init_template.py @@ -17,6 +17,7 @@ PLACEHOLDER_DIR = [Path("src/module_name/sample_data")] PYPROJECT_TARGET = Path("pyproject.toml") README_TARGET = Path("README.md") +CONTRIBUTING_TARGET = Path("CONTRIBUTING.md") NOX_TARGET = Path("noxfile.py") ALT_FILE_DIR = Path("alt_files") REQUIREMENTS_DIR = Path("requirements") @@ -111,6 +112,20 @@ def replace_readme_values(data: ProjectData) -> None: README_TARGET.write_text(readme) +@bookends("Updating references in CONTRIBUTING.md") +def replace_contributing_values(data: ProjectData) -> None: + """Update badge urls and placeholders in README.md""" + readme = CONTRIBUTING_TARGET.read_text() + default = ProjectData() + + readme = re.sub(ORG, data.org_name, readme) + readme = re.sub(REPO, data.repo_name, readme) + readme = re.sub(re.escape(default.org_name), data.org_name, readme) + readme = re.sub(re.escape(default.repo_name), data.repo_name, readme) + + CONTRIBUTING_TARGET.write_text(readme) + + @bookends("Updating noxfile.py values") def replace_nox_values(data: ProjectData) -> None: """Update nox value, replacing module_name with actual module name.""" @@ -134,6 +149,7 @@ def rename_module_folder(name: str) -> None: replace_pyproject_values(project_data) replace_nox_values(project_data) replace_readme_values(project_data) + replace_contributing_values(project_data) delete_placeholder_files() delete_placeholder_directories() From 34a3e4aabdcbcda511e0a156b9c08e104be89430 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 2 Aug 2024 23:29:08 -0400 Subject: [PATCH 03/28] 2024 Does this actually need to be updated? --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 0af0ae9..165c507 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Preocts +Copyright (c) 2024 Preocts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 7ccb455e70c7336ec4010cb9fd153e1cfd3150b5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 21:53:06 +0000 Subject: [PATCH 04/28] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1) - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d41ed7..b8a3336 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: # Flake8 for linting, line-length adjusted to match Black default - repo: https://github.com/PyCQA/flake8 - rev: 7.1.0 + rev: 7.1.1 hooks: - id: flake8 additional_dependencies: @@ -45,6 +45,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.1 hooks: - id: mypy From ce9bb9db1de65189f385cad1ac6412907d97ffc0 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 21:57:29 -0500 Subject: [PATCH 05/28] End of year clean up (#177) * Update pre-commit-config Version update (autoupdate) Remove hooks `debug-statements` and `mixed-line-endings` as rarely used. * Remove development requirements Both `black` and `flake8` are enforced by pre-commit in the CI and are not requirements for developing. `mypy` remains here for support of editor setups that have extensions to use mypy while working but require the library to be installed. * Use venv to ensure pip is updated Use the venv `--update-deps` flag to ensure pip is updated when a new venv is created. * Update black configuration Allow line lengths of 100 for black and remove flake8's config on length. flake8 is already configured to ignore line length violations. Reasoning of 100: A line length of 88 is the default which is 8 more than PEP-8 recommendations. This comes from the default recommendation plus two indents worth which amount to a class and method. 100 allows for three more indents. The goal here is not to encourage deeper nesting but to encourage more verbose naming without trying to trim those few characteres. Overall expectation is to keep using a visual guide of 88 characters. * Update github action versions Adds the flag `include-hidden-files: true` to the upload artifact action as behavior was changed in 4.4.1. --- .flake8 | 1 - .github/workflows/python-tests.yml | 25 +++++++++++++------------ .pre-commit-config.yaml | 8 +++----- noxfile.py | 5 ++--- pyproject.toml | 4 ++++ requirements/requirements-dev.txt | 5 ----- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/.flake8 b/.flake8 index d9b0b18..4d102bd 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,3 @@ extend-ignore = E501 E203 -max-line-length = 88 diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index b0efbd2..20e7924 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -1,9 +1,9 @@ name: "python tests and coverage" # Uses: -# https://github.com/actions/setup-python -# https://github.com/actions/checkout -# https://github.com/actions/download-artifact -# https://github.com/actions/upload-artifact +# https://github.com/actions/setup-python : 0b93645e9fea7318ecaed2b359559ac225c90a2b +# https://github.com/actions/checkout : 11bd71901bbe5b1630ceea73d27597364c9af683 +# https://github.com/actions/download-artifact : fa0a91b85d4f404e444e00e005971372dc801d16 +# https://github.com/actions/upload-artifact : 6f51ac03b9356f520e9adb1b1b7802705f340c2b on: pull_request: @@ -33,10 +33,10 @@ jobs: steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python ${{ matrix.python-version }}" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -50,11 +50,12 @@ jobs: nox --session tests_with_coverage-${{ matrix.python-version }} - name: "Save coverage artifact" - uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" + uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" with: name: "coverage-artifact-${{ matrix.os}}-${{ matrix.python-version}}" path: ".coverage.*" retention-days: 1 + include-hidden-files: true coverage-compile: name: "coverage compile" @@ -62,10 +63,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "3.12" @@ -74,7 +75,7 @@ jobs: python -m pip install --upgrade pip nox - name: "Download coverage artifacts" - uses: "actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427" + uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" with: pattern: "coverage-artifact-*" merge-multiple: true @@ -91,10 +92,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "3.12" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8a3336..1be4fc5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: # Batch of helpful formatters and patterns - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v4.6.0" + rev: "v5.0.0" hooks: - id: check-json - id: check-toml @@ -12,8 +12,6 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: check-docstring-first - - id: debug-statements - - id: mixed-line-ending # Adds a standard feel to import segments - repo: https://github.com/pycqa/isort @@ -29,7 +27,7 @@ repos: # Format code. No, I don't like everything black does either. - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.10.0 hooks: - id: black @@ -45,6 +43,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.14.0 hooks: - id: mypy diff --git a/noxfile.py b/noxfile.py index a64f157..f9088a7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -107,13 +107,12 @@ def install(session: nox.Session) -> None: activate_command = f"source {venv_path}/activate" if not os.path.exists(VENV_PATH): - session.run(py_command, "-m", "venv", VENV_PATH) - session.run(f"{venv_path}/python", "-m", "pip", "install", "--upgrade", "pip") + session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") session.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]") session.run(f"{venv_path}/pre-commit", "install") - if not os.environ.get("VIRTUAL_ENV"): + if not venv_path: session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") diff --git a/pyproject.toml b/pyproject.toml index 284d9dc..2fd37ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,10 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" # [project.scripts] # python-src-example = "module_name.sample:main" +[tool.black] +line-length = 100 +target-version = ['py39'] + [tool.setuptools.package-data] "module_name" = ["py.typed"] diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index a764745..c109579 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,9 +1,4 @@ # Development Requirements - linting, formatting, etc. pre-commit -black mypy -flake8 -flake8-builtins -flake8-pep585 -pep8-naming From eae084b054fc5ac0cd692fb7eb979f391b497822 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 22:00:44 -0500 Subject: [PATCH 06/28] Revert "End of year clean up (#177)" (#178) This reverts commit ce9bb9db1de65189f385cad1ac6412907d97ffc0. --- .flake8 | 1 + .github/workflows/python-tests.yml | 25 ++++++++++++------------- .pre-commit-config.yaml | 8 +++++--- noxfile.py | 5 +++-- pyproject.toml | 4 ---- requirements/requirements-dev.txt | 5 +++++ 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.flake8 b/.flake8 index 4d102bd..d9b0b18 100644 --- a/.flake8 +++ b/.flake8 @@ -4,3 +4,4 @@ extend-ignore = E501 E203 +max-line-length = 88 diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 20e7924..b0efbd2 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -1,9 +1,9 @@ name: "python tests and coverage" # Uses: -# https://github.com/actions/setup-python : 0b93645e9fea7318ecaed2b359559ac225c90a2b -# https://github.com/actions/checkout : 11bd71901bbe5b1630ceea73d27597364c9af683 -# https://github.com/actions/download-artifact : fa0a91b85d4f404e444e00e005971372dc801d16 -# https://github.com/actions/upload-artifact : 6f51ac03b9356f520e9adb1b1b7802705f340c2b +# https://github.com/actions/setup-python +# https://github.com/actions/checkout +# https://github.com/actions/download-artifact +# https://github.com/actions/upload-artifact on: pull_request: @@ -33,10 +33,10 @@ jobs: steps: - name: "Repo checkout" - uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" + uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" - name: "Set up Python ${{ matrix.python-version }}" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -50,12 +50,11 @@ jobs: nox --session tests_with_coverage-${{ matrix.python-version }} - name: "Save coverage artifact" - uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" + uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" with: name: "coverage-artifact-${{ matrix.os}}-${{ matrix.python-version}}" path: ".coverage.*" retention-days: 1 - include-hidden-files: true coverage-compile: name: "coverage compile" @@ -63,10 +62,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" + uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" - name: "Set up Python" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" with: python-version: "3.12" @@ -75,7 +74,7 @@ jobs: python -m pip install --upgrade pip nox - name: "Download coverage artifacts" - uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" + uses: "actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427" with: pattern: "coverage-artifact-*" merge-multiple: true @@ -92,10 +91,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" + uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" - name: "Set up Python" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" with: python-version: "3.12" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1be4fc5..b8a3336 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: # Batch of helpful formatters and patterns - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v5.0.0" + rev: "v4.6.0" hooks: - id: check-json - id: check-toml @@ -12,6 +12,8 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: check-docstring-first + - id: debug-statements + - id: mixed-line-ending # Adds a standard feel to import segments - repo: https://github.com/pycqa/isort @@ -27,7 +29,7 @@ repos: # Format code. No, I don't like everything black does either. - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 + rev: 24.4.2 hooks: - id: black @@ -43,6 +45,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.0 + rev: v1.11.1 hooks: - id: mypy diff --git a/noxfile.py b/noxfile.py index f9088a7..a64f157 100644 --- a/noxfile.py +++ b/noxfile.py @@ -107,12 +107,13 @@ def install(session: nox.Session) -> None: activate_command = f"source {venv_path}/activate" if not os.path.exists(VENV_PATH): - session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") + session.run(py_command, "-m", "venv", VENV_PATH) + session.run(f"{venv_path}/python", "-m", "pip", "install", "--upgrade", "pip") session.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]") session.run(f"{venv_path}/pre-commit", "install") - if not venv_path: + if not os.environ.get("VIRTUAL_ENV"): session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") diff --git a/pyproject.toml b/pyproject.toml index 2fd37ca..284d9dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,10 +38,6 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" # [project.scripts] # python-src-example = "module_name.sample:main" -[tool.black] -line-length = 100 -target-version = ['py39'] - [tool.setuptools.package-data] "module_name" = ["py.typed"] diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index c109579..a764745 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,4 +1,9 @@ # Development Requirements - linting, formatting, etc. pre-commit +black mypy +flake8 +flake8-builtins +flake8-pep585 +pep8-naming From f4feab56836a1f92f3b418b5f5c5e996275f2988 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 20:58:13 -0500 Subject: [PATCH 07/28] Update pre-commit-config Version update (autoupdate) Remove hooks `debug-statements` and `mixed-line-endings` as rarely used. --- .pre-commit-config.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b8a3336..1be4fc5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: # Batch of helpful formatters and patterns - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v4.6.0" + rev: "v5.0.0" hooks: - id: check-json - id: check-toml @@ -12,8 +12,6 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: check-docstring-first - - id: debug-statements - - id: mixed-line-ending # Adds a standard feel to import segments - repo: https://github.com/pycqa/isort @@ -29,7 +27,7 @@ repos: # Format code. No, I don't like everything black does either. - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.10.0 hooks: - id: black @@ -45,6 +43,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.14.0 hooks: - id: mypy From 6a52cff6c65354d4f4a9b306df18d3e56aa96356 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 21:15:18 -0500 Subject: [PATCH 08/28] Remove development requirements Both `black` and `flake8` are enforced by pre-commit in the CI and are not requirements for developing. `mypy` remains here for support of editor setups that have extensions to use mypy while working but require the library to be installed. --- requirements/requirements-dev.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index a764745..c109579 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,9 +1,4 @@ # Development Requirements - linting, formatting, etc. pre-commit -black mypy -flake8 -flake8-builtins -flake8-pep585 -pep8-naming From fdce9982203380de81efd900a999a20c8c5a9993 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 21:22:38 -0500 Subject: [PATCH 09/28] Use venv to ensure pip is updated Use the venv `--update-deps` flag to ensure pip is updated when a new venv is created. --- noxfile.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index a64f157..f9088a7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -107,13 +107,12 @@ def install(session: nox.Session) -> None: activate_command = f"source {venv_path}/activate" if not os.path.exists(VENV_PATH): - session.run(py_command, "-m", "venv", VENV_PATH) - session.run(f"{venv_path}/python", "-m", "pip", "install", "--upgrade", "pip") + session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") session.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]") session.run(f"{venv_path}/pre-commit", "install") - if not os.environ.get("VIRTUAL_ENV"): + if not venv_path: session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") From e36f3bf4db87025f9f554d9b5fd8251216c532c3 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 21:27:54 -0500 Subject: [PATCH 10/28] Update black configuration Allow line lengths of 100 for black and remove flake8's config on length. flake8 is already configured to ignore line length violations. Reasoning of 100: A line length of 88 is the default which is 8 more than PEP-8 recommendations. This comes from the default recommendation plus two indents worth which amount to a class and method. 100 allows for three more indents. The goal here is not to encourage deeper nesting but to encourage more verbose naming without trying to trim those few characteres. Overall expectation is to keep using a visual guide of 88 characters. --- .flake8 | 1 - pyproject.toml | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index d9b0b18..4d102bd 100644 --- a/.flake8 +++ b/.flake8 @@ -4,4 +4,3 @@ extend-ignore = E501 E203 -max-line-length = 88 diff --git a/pyproject.toml b/pyproject.toml index 284d9dc..2fd37ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,10 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" # [project.scripts] # python-src-example = "module_name.sample:main" +[tool.black] +line-length = 100 +target-version = ['py39'] + [tool.setuptools.package-data] "module_name" = ["py.typed"] From 37cf7e52cb6849fcb0251effd2395a6b008b8618 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 27 Dec 2024 21:35:20 -0500 Subject: [PATCH 11/28] Update github action versions Adds the flag `include-hidden-files: true` to the upload artifact action as behavior was changed in 4.4.1. --- .github/workflows/python-tests.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index b0efbd2..20e7924 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -1,9 +1,9 @@ name: "python tests and coverage" # Uses: -# https://github.com/actions/setup-python -# https://github.com/actions/checkout -# https://github.com/actions/download-artifact -# https://github.com/actions/upload-artifact +# https://github.com/actions/setup-python : 0b93645e9fea7318ecaed2b359559ac225c90a2b +# https://github.com/actions/checkout : 11bd71901bbe5b1630ceea73d27597364c9af683 +# https://github.com/actions/download-artifact : fa0a91b85d4f404e444e00e005971372dc801d16 +# https://github.com/actions/upload-artifact : 6f51ac03b9356f520e9adb1b1b7802705f340c2b on: pull_request: @@ -33,10 +33,10 @@ jobs: steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python ${{ matrix.python-version }}" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -50,11 +50,12 @@ jobs: nox --session tests_with_coverage-${{ matrix.python-version }} - name: "Save coverage artifact" - uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" + uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" with: name: "coverage-artifact-${{ matrix.os}}-${{ matrix.python-version}}" path: ".coverage.*" retention-days: 1 + include-hidden-files: true coverage-compile: name: "coverage compile" @@ -62,10 +63,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "3.12" @@ -74,7 +75,7 @@ jobs: python -m pip install --upgrade pip nox - name: "Download coverage artifacts" - uses: "actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427" + uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" with: pattern: "coverage-artifact-*" merge-multiple: true @@ -91,10 +92,10 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Repo checkout" - uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c" + uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: python-version: "3.12" From be867489d21317ca3b18a901e23e8889d65907a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 15:36:36 -0500 Subject: [PATCH 12/28] [pre-commit.ci] pre-commit autoupdate (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.14.0 → v1.14.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.0...v1.14.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1be4fc5..ba2de1a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.0 + rev: v1.14.1 hooks: - id: mypy From 90293163ea7611bde484b2e90c3ad75eaa12f799 Mon Sep 17 00:00:00 2001 From: Preocts Date: Sat, 11 Jan 2025 21:57:26 -0500 Subject: [PATCH 13/28] 2025 (#182) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 165c507..795f190 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Preocts +Copyright (c) 2025 Preocts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c4f380d864e0abff989707ff541ec183f6708277 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 28 Feb 2025 02:22:43 -0500 Subject: [PATCH 14/28] Small updates (#184) * Udpate .gitignore Simply and clean. Also moving to the default ./venv to ./.venv * Tidy pyproject.toml * Simplify noxfile session names * Update documentation --- .github/workflows/python-tests.yml | 6 +++--- .gitignore | 27 ++++++++++++------------ CONTRIBUTING.md | 6 +++--- README.md | 13 ++++++------ noxfile.py | 33 +++++++++++++++--------------- pyproject.toml | 13 ++++++------ 6 files changed, 50 insertions(+), 48 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 20e7924..890e754 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -47,7 +47,7 @@ jobs: - name: "Run tests and coverage via nox" run: | - nox --session tests_with_coverage-${{ matrix.python-version }} + nox --session version_coverage-${{ matrix.python-version }} - name: "Save coverage artifact" uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" @@ -82,7 +82,7 @@ jobs: - name: "Compile coverage data, print report" run: | - nox --session coverage_combine_and_report + nox --session coverage_combine export TOTAL=$(python -c "import json;print(json.load(open('coverage.json'))['totals']['percent_covered_display'])") echo "TOTAL=$TOTAL" >> $GITHUB_ENV echo "### Total coverage: ${TOTAL}%" >> $GITHUB_STEP_SUMMARY @@ -105,4 +105,4 @@ jobs: - name: "Enforce strict type annotations with mypy" run: | - nox --session mypy_check + nox --session mypy diff --git a/.gitignore b/.gitignore index 0f91802..e448f26 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,15 @@ -temp_* -.env -venv/ *egg-info/ -.nox/ -.coverage -.coverage.* -coverage.xml -coverage.json -htmlcov/ -build/ -dist/ -__pycache__/ -.vscode/ +*.pyc + +build +dist + +.vscode +.nox +htmlcov +.coverage* +coverage.* + +.venv* +temp* +.env diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3083c4..e5392e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,17 +72,17 @@ the desired version while creating the `venv`. (e.g. `python3` or `python3.12`) ### Create the `venv`: ```console -python -m venv venv +python -m venv .venv ``` Activate the `venv`: ```console # Linux/Mac -. venv/bin/activate +. .venv/bin/activate # Windows -venv\Scripts\activate +.venv\Scripts\activate ``` The command prompt should now have a `(venv)` prefix on it. `python` will now diff --git a/README.md b/README.md index 40312e7..5352c56 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,6 @@ fit. one-size-fits-most template I've put together. Use what you want how you want. -- **Q:** Why do you hard pin your development and test requirements? - - **A:** For control over the environment used to develop on the package. It - is also beneficial in many of the areas I work where artifactory proxies are - between `pip` and the pypi public index. Versions remaining hard pinned - ensure the package is always cleared for use through the artifactory. - - **Q:** Why not put the requirements into the `pyproject.toml`? - **A:** Mostly because `pip-compile` does all the work for me and doesn't target the `pyproject.toml`. Partly because many of my projects need to be @@ -37,3 +31,10 @@ fit. - **A:** I'm constantly finding new tweaks that make the template fit just a little better. I'm also open to ideas and suggestions so please drop an issue if you have one. + +- **Q:** Have I heard of uv? + - **A:** Yes. I'm already exploring a uv driven workflow for this template. + You can see it on the `uv-workflow` branch. I will be waiting until the + April 2025 release of pip for support of + [dependency-groups](https://packaging.python.org/en/latest/specifications/dependency-groups/) + before committing to any changes. diff --git a/noxfile.py b/noxfile.py index f9088a7..e5224c5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -13,7 +13,8 @@ COVERAGE_FAIL_UNDER = 50 DEFAULT_PYTHON_VERSION = "3.12" PYTHON_MATRIX = ["3.9", "3.10", "3.11", "3.12", "3.13"] -VENV_PATH = "venv" +VENV_BACKEND = "venv" +VENV_PATH = ".venv" REQUIREMENT_IN_FILES = [ pathlib.Path("requirements/requirements.in"), ] @@ -36,14 +37,14 @@ # Define the default sessions run when `nox` is called on the CLI nox.options.sessions = [ - "tests_with_coverage", - "coverage_combine_and_report", - "mypy_check", + "version_coverage", + "coverage_combine", + "mypy", ] -@nox.session(python=PYTHON_MATRIX) -def tests_with_coverage(session: nox.Session) -> None: +@nox.session(python=PYTHON_MATRIX, venv_backend=VENV_BACKEND) +def version_coverage(session: nox.Session) -> None: """Run unit tests with coverage saved to partial file.""" print_standard_logs(session) @@ -51,8 +52,8 @@ def tests_with_coverage(session: nox.Session) -> None: session.run("coverage", "run", "-p", "-m", "pytest", TESTS_PATH) -@nox.session(python=DEFAULT_PYTHON_VERSION) -def coverage_combine_and_report(session: nox.Session) -> None: +@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) +def coverage_combine(session: nox.Session) -> None: """Combine all coverage partial files and generate JSON report.""" print_standard_logs(session) @@ -64,8 +65,8 @@ def coverage_combine_and_report(session: nox.Session) -> None: session.run("python", "-m", "coverage", "json") -@nox.session(python=DEFAULT_PYTHON_VERSION) -def mypy_check(session: nox.Session) -> None: +@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) +def mypy(session: nox.Session) -> None: """Run mypy against package and all required dependencies.""" print_standard_logs(session) @@ -74,7 +75,7 @@ def mypy_check(session: nox.Session) -> None: session.run("mypy", "-p", MODULE_NAME, "--no-incremental") -@nox.session(python=False) +@nox.session(python=False, venv_backend=VENV_BACKEND) def coverage(session: nox.Session) -> None: """Generate a coverage report. Does not use a venv.""" session.run("coverage", "erase") @@ -82,7 +83,7 @@ def coverage(session: nox.Session) -> None: session.run("coverage", "report", "-m") -@nox.session(python=DEFAULT_PYTHON_VERSION) +@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) def build(session: nox.Session) -> None: """Build distribution files.""" print_standard_logs(session) @@ -91,7 +92,7 @@ def build(session: nox.Session) -> None: session.run("python", "-m", "build") -@nox.session(python=False) +@nox.session(python=False, venv_backend=VENV_BACKEND) def install(session: nox.Session) -> None: """Setup a development environment. Uses active venv if available, builds one if not.""" # Use the active environement if it exists, otherwise create a new one @@ -116,7 +117,7 @@ def install(session: nox.Session) -> None: session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") -@nox.session(python=DEFAULT_PYTHON_VERSION) +@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) def update(session: nox.Session) -> None: """Process requirement*.in files, updating only additions/removals.""" print_standard_logs(session) @@ -126,7 +127,7 @@ def update(session: nox.Session) -> None: session.run("pip-compile", "--no-emit-index-url", str(filename)) -@nox.session(python=DEFAULT_PYTHON_VERSION) +@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) def upgrade(session: nox.Session) -> None: """Process requirement*.in files and upgrade all libraries as possible.""" print_standard_logs(session) @@ -136,7 +137,7 @@ def upgrade(session: nox.Session) -> None: session.run("pip-compile", "--no-emit-index-url", "--upgrade", str(filename)) -@nox.session(python=False) +@nox.session(python=False, venv_backend=VENV_BACKEND) def clean(_: nox.Session) -> None: """Clean cache, .pyc, .pyo, and test/build artifact files from project.""" count = 0 diff --git a/pyproject.toml b/pyproject.toml index 2fd37ca..f8487b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,12 @@ classifiers = [ ] dynamic = ["dependencies", "optional-dependencies", "version"] +[project.urls] +homepage = "https://github.com/[ORG NAME]/[REPO NAME]" + +# [project.scripts] +# python-src-example = "module_name.sample:main" + [tool.setuptools_scm] # Purposely left empty @@ -31,13 +37,6 @@ file = ["requirements/requirements.txt"] dev = { file = ["requirements/requirements-dev.txt"] } test = { file = ["requirements/requirements-test.txt"] } -[project.urls] -homepage = "https://github.com/[ORG NAME]/[REPO NAME]" - -# CLI scripts if needed -# [project.scripts] -# python-src-example = "module_name.sample:main" - [tool.black] line-length = 100 target-version = ['py39'] From 9ef18ab51dcc3a25b1f9382ca627f81abdfce7ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Feb 2025 03:21:14 -0500 Subject: [PATCH 15/28] [pre-commit.ci] pre-commit autoupdate (#183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.13.2 → 6.0.0](https://github.com/pycqa/isort/compare/5.13.2...6.0.0) - [github.com/psf/black-pre-commit-mirror: 24.10.0 → 25.1.0](https://github.com/psf/black-pre-commit-mirror/compare/24.10.0...25.1.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba2de1a..df1822c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: # Adds a standard feel to import segments - repo: https://github.com/pycqa/isort - rev: 5.13.2 + rev: 6.0.0 hooks: - id: isort args: @@ -27,7 +27,7 @@ repos: # Format code. No, I don't like everything black does either. - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 + rev: 25.1.0 hooks: - id: black From 768ba1e018749e3ffaca17089637a2b810a72c52 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 21:42:44 -0500 Subject: [PATCH 16/28] [pre-commit.ci] pre-commit autoupdate (#186) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 6.0.0 → 6.0.1](https://github.com/pycqa/isort/compare/6.0.0...6.0.1) - [github.com/PyCQA/flake8: 7.1.1 → 7.1.2](https://github.com/PyCQA/flake8/compare/7.1.1...7.1.2) - [github.com/pre-commit/mirrors-mypy: v1.14.1 → v1.15.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.14.1...v1.15.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df1822c..9e5de32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: # Adds a standard feel to import segments - repo: https://github.com/pycqa/isort - rev: 6.0.0 + rev: 6.0.1 hooks: - id: isort args: @@ -33,7 +33,7 @@ repos: # Flake8 for linting, line-length adjusted to match Black default - repo: https://github.com/PyCQA/flake8 - rev: 7.1.1 + rev: 7.1.2 hooks: - id: flake8 additional_dependencies: @@ -43,6 +43,6 @@ repos: # Type enforcement for Python - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.14.1 + rev: v1.15.0 hooks: - id: mypy From 6b34d2039c477fbdb389aafc9191122678d717c9 Mon Sep 17 00:00:00 2001 From: Preocts Date: Thu, 29 May 2025 02:36:18 -0400 Subject: [PATCH 17/28] Dependency management improvements (#188) * Correctly use a constraints file Turns out I've been using the constraint file incorrectly for years. I love learning moments like this! The `constraints.txt` file **is** the lockfile for the project. It contains all the versions that *should* be used *if* they are needed. Is is not the source of **what** should be installed, only what version. So, instead of having N different `requirements*.in` files with N different `requirements*.txt` generated from `pip-compile`; we have a single `constraints.txt` that all of our `requirements*.txt` files reference. Glorious. This solves for the scaling problem that I ran into when, for example, `requirements-doc.txt` had a version conflicct with something in `requirements-test.txt`. Initially, I thought to somehow cross-reference all the `.txt` files with themselves in some horrible chained constraint system. That worked about as well as it sounds. Note, I have to remove the dynamic dependencies here from the `pyproject.toml`. That's fine. Eventually, I'll be moving to `uv` which simplifies most of this juggling. For now, however, I want to understand the underlying machanics before abstracting them away. * Improve noxfile for dev install and management Moving toward simplicity of use, the `noxfile.txt` now has a session called `dev`. This session creates, if needed, the `.venv` and installs all development requirements. Additionally, the `update` and `upgrade` sessions have been renamed to `update-deps` and `upgrade-deps` respectively. They now use the new, awesome pattern of a single constraint file for dependencies. Finally, added the `--no-annotations` to the `pip-compile` commands and what a difference that makes! I have yet to understand when and why `pip-compile` will expand `requirements/requirements.txt` to `/home/preocts/project-name/requirements/requirements.txt`. In practice, the results are different depending on the OS being used as well. Since it all lead to noisy diffs without much value, removing it seems ideal! * Correct nox sessions for CI actions Okay, this is the ugly part of the path I'm taking. It is now required to install the correct requirement*.txt files for each session. Previously it was a simple `.[test]` reference when installing the package. This too shall pass. Already thinking about pivoting into listing all requirements in the `pyproject.toml`. With dependency groups being supported this should be amazing. It will hindge on `pip-compile` being able to extract from dependency groups. That's the next PR! * Update documenation Adjust the contributing docs with the new expected nox workflows. Add some leading links at the top of the readme pointing to the secondary files. Reading, strangely, isn't the strongest skill of many. --- CONTRIBUTING.md | 89 ++++---------------- README.md | 5 ++ noxfile.py | 127 ++++++++++++++++++----------- pyproject.toml | 9 +- requirements/constraints.txt | 31 +++++++ requirements/requirements-dev.txt | 12 ++- requirements/requirements-test.txt | 2 +- requirements/requirements.in | 6 -- requirements/requirements.txt | 10 +-- 9 files changed, 148 insertions(+), 143 deletions(-) create mode 100644 requirements/constraints.txt delete mode 100644 requirements/requirements.in diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e5392e6..3ddec50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,6 @@ prior to submitting a pull request. - No test should be dependent on another - No test should be dependent on secrets/tokens - --- # Local developer installation @@ -52,81 +51,23 @@ git clone https://github.com/[ORG NAME]/[REPO NAME] cd [REPO NAME] ``` -### Virtual Environment - -Use a ([`venv`](https://docs.python.org/3/library/venv.html)), or equivalent, -when working with python projects. Leveraging a `venv` will ensure the installed -dependency files will not impact other python projects or any system -dependencies. - -**Windows users**: Depending on your python install you will use `py` in place -of `python` to create the `venv`. - -**Linux/Mac users**: Replace `python`, if needed, with the appropriate call to -the desired version while creating the `venv`. (e.g. `python3` or `python3.12`) - -**All users**: Once inside an active `venv` all systems should allow the use of -`python` for command line instructions. This will ensure you are using the -`venv`'s python and not the system level python. - -### Create the `venv`: - -```console -python -m venv .venv -``` - -Activate the `venv`: - -```console -# Linux/Mac -. .venv/bin/activate - -# Windows -.venv\Scripts\activate -``` +### [Install nox](https://nox.thea.codes/en/stable/index.html) -The command prompt should now have a `(venv)` prefix on it. `python` will now -call the version of the interpreter used to create the `venv` +It is recommended to use a tool such as `pipx` or `uvx` to install nox. nox is +needed to run the provided sessions for developer setup, linting, tests, and +dependency management. It is optional, but these instructions will not cover +manually steps outside of nox. -To deactivate (exit) the `venv`: -```console -deactivate -``` - ---- +## Nox Sessions -## Developer Installation Steps +### Developer Install -### Install editable library and development requirements +This builds the `/.venv`, installs the editable +package, and installs all dependency files. -```console -python -m pip install --editable .[dev,test] -``` - -### Install pre-commit [(see below for details)](#pre-commit) - -```console -pre-commit install -``` - -### Install with nox - -If you have `nox` installed with `pipx` or in the current venv you can use the -following session. This is an alternative to the two steps above. - -```console -nox -s install -``` - ---- - -## Pre-commit and nox tools - -### Run pre-commit on all files - -```console -pre-commit run --all-files +```bash +nox -s dev ``` ### Run tests with coverage (quick) @@ -151,23 +92,23 @@ nox -e build ## Updating dependencies -New dependencys can be added to the `requirements-*.in` file. It is recommended +New dependencys can be added to the `requirements-*.txt` file. It is recommended to only use pins when specific versions or upgrades beyond a certain version are to be avoided. Otherwise, allow `pip-compile` to manage the pins in the -generated `requirements-*.txt` files. +generated `constraints.txt` file. Once updated following the steps below, the package can be installed if needed. ### Update the generated files with changes ```console -nox -e update +nox -s update-deps ``` ### Upgrade all generated dependencies ```console -nox -e upgrade +nox -s upgrade-deps ``` --- diff --git a/README.md b/README.md index 5352c56..3915202 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,11 @@ # python-src-template +- [Contributing Guide and Developer Setup Guide](./CONTRIBUTING.md) +- [License: MIT](./LICENSE) + +--- + A template I use for most projects and is setup to jive with my environment at the company I work with. diff --git a/noxfile.py b/noxfile.py index e5224c5..d5ff087 100644 --- a/noxfile.py +++ b/noxfile.py @@ -11,13 +11,11 @@ MODULE_NAME = "module_name" TESTS_PATH = "tests" COVERAGE_FAIL_UNDER = 50 -DEFAULT_PYTHON_VERSION = "3.12" +DEFAULT_PYTHON = "3.12" PYTHON_MATRIX = ["3.9", "3.10", "3.11", "3.12", "3.13"] VENV_BACKEND = "venv" VENV_PATH = ".venv" -REQUIREMENT_IN_FILES = [ - pathlib.Path("requirements/requirements.in"), -] +REQUIREMENTS_PATH = "./requirements" # What we allowed to clean (delete) CLEANABLE_TARGETS = [ @@ -43,35 +41,72 @@ ] +@nox.session(python=False) +def dev(session: nox.Session) -> None: + """Setup a development environment by creating the venv and installs dependencies.""" + # Use the active environement if it exists, otherwise create a new one + venv_path = os.environ.get("VIRTUAL_ENV", VENV_PATH) + + if sys.platform == "win32": + py_command = "py" + venv_path = f"{venv_path}/Scripts" + activate_command = f"{venv_path}/activate" + else: + py_command = f"python{DEFAULT_PYTHON}" + venv_path = f"{venv_path}/bin" + activate_command = f"source {venv_path}/activate" + + if not os.path.exists(VENV_PATH): + session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") + + python = f"{venv_path}/python" + requirement_files = get_requirement_files() + + session.run(python, "-m", "pip", "install", "-e", ".") + for requirement_file in requirement_files: + session.run(python, "-m", "pip", "install", "-r", requirement_file) + + session.run(python, "-m", "pip", "install", "pre-commit") + session.run(f"{venv_path}/pre-commit", "install") + + if not os.environ.get("VIRTUAL_ENV"): + session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") + + @nox.session(python=PYTHON_MATRIX, venv_backend=VENV_BACKEND) def version_coverage(session: nox.Session) -> None: """Run unit tests with coverage saved to partial file.""" print_standard_logs(session) - session.install(".[test]") + session.install(".") + session.install("-r", "requirements/requirements.txt") + session.install("-r", "requirements/requirements-test.txt") session.run("coverage", "run", "-p", "-m", "pytest", TESTS_PATH) -@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) +@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) def coverage_combine(session: nox.Session) -> None: """Combine all coverage partial files and generate JSON report.""" print_standard_logs(session) fail_under = f"--fail-under={COVERAGE_FAIL_UNDER}" - session.install(".[test]") + session.install(".") + session.install("-r", "requirements/requirements.txt") + session.install("-r", "requirements/requirements-test.txt") session.run("python", "-m", "coverage", "combine") session.run("python", "-m", "coverage", "report", "-m", fail_under) session.run("python", "-m", "coverage", "json") -@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) +@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) def mypy(session: nox.Session) -> None: """Run mypy against package and all required dependencies.""" print_standard_logs(session) session.install(".") - session.install("mypy") + session.install("-r", "requirements/requirements.txt") + session.install("-r", "requirements/requirements-dev.txt") session.run("mypy", "-p", MODULE_NAME, "--no-incremental") @@ -83,7 +118,7 @@ def coverage(session: nox.Session) -> None: session.run("coverage", "report", "-m") -@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) +@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) def build(session: nox.Session) -> None: """Build distribution files.""" print_standard_logs(session) @@ -92,49 +127,41 @@ def build(session: nox.Session) -> None: session.run("python", "-m", "build") -@nox.session(python=False, venv_backend=VENV_BACKEND) -def install(session: nox.Session) -> None: - """Setup a development environment. Uses active venv if available, builds one if not.""" - # Use the active environement if it exists, otherwise create a new one - venv_path = os.environ.get("VIRTUAL_ENV", VENV_PATH) - - if sys.platform == "win32": - py_command = "py" - venv_path = f"{venv_path}/Scripts" - activate_command = f"{venv_path}/activate" - else: - py_command = f"python{DEFAULT_PYTHON_VERSION}" - venv_path = f"{venv_path}/bin" - activate_command = f"source {venv_path}/activate" - - if not os.path.exists(VENV_PATH): - session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") - - session.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]") - session.run(f"{venv_path}/pre-commit", "install") - - if not venv_path: - session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") - - -@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) -def update(session: nox.Session) -> None: - """Process requirement*.in files, updating only additions/removals.""" +@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="update-deps") +def update_deps(session: nox.Session) -> None: + """Process requirement*.txt files, updating only additions/removals.""" print_standard_logs(session) - session.install("pip-tools") - for filename in REQUIREMENT_IN_FILES: - session.run("pip-compile", "--no-emit-index-url", str(filename)) - + requirement_files = get_requirement_files() -@nox.session(python=DEFAULT_PYTHON_VERSION, venv_backend=VENV_BACKEND) -def upgrade(session: nox.Session) -> None: - """Process requirement*.in files and upgrade all libraries as possible.""" + session.install("pip-tools") + session.run( + "pip-compile", + "--no-annotate", + "--no-emit-index-url", + "--output-file", + f"{REQUIREMENTS_PATH}/constraints.txt", + *requirement_files, + ) + + +@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="upgrade-deps") +def upgrade_deps(session: nox.Session) -> None: + """Process requirement*.txt files and upgrade all libraries as possible.""" print_standard_logs(session) + requirement_files = get_requirement_files() + session.install("pip-tools") - for filename in REQUIREMENT_IN_FILES: - session.run("pip-compile", "--no-emit-index-url", "--upgrade", str(filename)) + session.run( + "pip-compile", + "--no-annotate", + "--no-emit-index-url", + "--upgrade", + "--output-file", + f"{REQUIREMENTS_PATH}/constraints.txt", + *requirement_files, + ) @nox.session(python=False, venv_backend=VENV_BACKEND) @@ -157,3 +184,9 @@ def print_standard_logs(session: nox.Session) -> None: version = session.run("python", "--version", silent=True) session.log(f"Running from: {session.bin}") session.log(f"Running with: {version}") + + +def get_requirement_files() -> list[pathlib.Path]: + """Get a list of requirement files matching "requirements*.txt".""" + glob = pathlib.Path(REQUIREMENTS_PATH).glob("requirements*.txt") + return [path for path in glob] diff --git a/pyproject.toml b/pyproject.toml index f8487b7..2ac4a93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", ] -dynamic = ["dependencies", "optional-dependencies", "version"] +dynamic = ["version"] [project.urls] homepage = "https://github.com/[ORG NAME]/[REPO NAME]" @@ -30,13 +30,6 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" [tool.setuptools_scm] # Purposely left empty -[tool.setuptools.dynamic.dependencies] -file = ["requirements/requirements.txt"] - -[tool.setuptools.dynamic.optional-dependencies] -dev = { file = ["requirements/requirements-dev.txt"] } -test = { file = ["requirements/requirements-test.txt"] } - [tool.black] line-length = 100 target-version = ['py39'] diff --git a/requirements/constraints.txt b/requirements/constraints.txt new file mode 100644 index 0000000..ad74fee --- /dev/null +++ b/requirements/constraints.txt @@ -0,0 +1,31 @@ +# +# This file is autogenerated by pip-compile with Python 3.12 +# by the following command: +# +# pip-compile --no-annotate --no-emit-index-url --output-file=./requirements/constraints.txt requirements/requirements-dev.txt requirements/requirements-test.txt requirements/requirements.txt +# +black==25.1.0 +certifi==2025.4.26 +charset-normalizer==3.4.2 +click==8.2.1 +coverage==7.8.2 +flake8==7.2.0 +flake8-builtins==2.5.0 +flake8-pep585==0.1.7 +idna==3.10 +iniconfig==2.1.0 +isort==6.0.1 +mccabe==0.7.0 +mypy==1.15.0 +mypy-extensions==1.1.0 +packaging==25.0 +pathspec==0.12.1 +platformdirs==4.3.8 +pluggy==1.6.0 +pycodestyle==2.13.0 +pyflakes==3.3.2 +pytest==8.3.5 +pytest-randomly==3.16.0 +requests==2.32.3 +typing-extensions==4.13.2 +urllib3==2.4.0 diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index c109579..cabf4c1 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,4 +1,14 @@ # Development Requirements - linting, formatting, etc. +--constraint ./constraints.txt -pre-commit +# Linting +flake8 +flake8-builtins +flake8-pep585 + +# formatters +black +isort + +# type checking mypy diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index e6dafae..b01a13f 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,6 +1,6 @@ # Testing Requirements +--constraint ./constraints.txt pytest pytest-randomly coverage -nox diff --git a/requirements/requirements.in b/requirements/requirements.in deleted file mode 100644 index e17206f..0000000 --- a/requirements/requirements.in +++ /dev/null @@ -1,6 +0,0 @@ -# Core Dependencies -# ----------------- -# Ensure to set PIP_INDEX_URL to the correct value for your environment -# This is the URL to the Artifactory instance that hosts the Python packages (default: pypi.org) -# This will not be emitted to the requirements*.txt files and must be set in the environment -# before running pip install diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 48a4bfc..a0d3e12 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,6 +1,4 @@ -# -# This file is autogenerated by pip-compile with Python 3.12 -# by the following command: -# -# pip-compile --no-emit-index-url requirements/requirements.in -# +# Core Dependencies +--constraint ./constraints.txt + +requests From a7743de0bb6ed4f85c0696cdc9a536797a276c85 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 01:38:19 -0400 Subject: [PATCH 18/28] Refactoring noxfile for testing (#189) * Use nox.options instead of global variable Move the declaration of the backend to a `nox.options` declaration instead of passing an extra variable to each `@session`. * Refactor running tests with nox Instead of having a session that handles a large matrix of python versions, use a session that leverages the default python. This reduces the overhead of maintaining the `noxfile.py`. The replacement session has an optional posarg `partial-coverage` which will produce parallel-mode coverage files. This creates drop-in replacement behavior for the GitHub action which runs the tests on its own matrix of python versions and operating systems. `coverage_combine` moves to a CI only session. * Remove cache: pip option This is having a negative impact to the CI. Will investigate later --- .github/workflows/python-tests.yml | 10 ++--- noxfile.py | 63 ++++++++++++++---------------- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 890e754..4aeed51 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -15,7 +15,7 @@ on: jobs: run-tests-and-coverage: - name: "Run nox for tests and coverage" + name: "Run pytest with coverage." runs-on: "${{ matrix.os }}" strategy: fail-fast: false @@ -43,11 +43,11 @@ jobs: - name: "Install nox" run: | - python -m pip install --upgrade pip nox + python -m pip install --upgrade nox - name: "Run tests and coverage via nox" run: | - nox --session version_coverage-${{ matrix.python-version }} + nox --session test -- partial-coverage - name: "Save coverage artifact" uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" @@ -58,7 +58,7 @@ jobs: include-hidden-files: true coverage-compile: - name: "coverage compile" + name: "Compile coverage reports." needs: "run-tests-and-coverage" runs-on: "ubuntu-latest" steps: @@ -72,7 +72,7 @@ jobs: - name: "Install nox" run: | - python -m pip install --upgrade pip nox + python -m pip install --upgrade nox - name: "Download coverage artifacts" uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" diff --git a/noxfile.py b/noxfile.py index d5ff087..658df4c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,6 +4,7 @@ import pathlib import shutil import sys +from functools import partial import nox @@ -12,8 +13,6 @@ TESTS_PATH = "tests" COVERAGE_FAIL_UNDER = 50 DEFAULT_PYTHON = "3.12" -PYTHON_MATRIX = ["3.9", "3.10", "3.11", "3.12", "3.13"] -VENV_BACKEND = "venv" VENV_PATH = ".venv" REQUIREMENTS_PATH = "./requirements" @@ -32,11 +31,10 @@ "./**/*.pyo", ] - # Define the default sessions run when `nox` is called on the CLI +nox.options.default_venv_backend = "virtualenv" nox.options.sessions = [ - "version_coverage", - "coverage_combine", + "test", "mypy", ] @@ -73,33 +71,40 @@ def dev(session: nox.Session) -> None: session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") -@nox.session(python=PYTHON_MATRIX, venv_backend=VENV_BACKEND) -def version_coverage(session: nox.Session) -> None: - """Run unit tests with coverage saved to partial file.""" +@nox.session(name="test") +def run_tests_with_coverage(session: nox.Session) -> None: + """Run pytest with coverage, outputs console report and json.""" print_standard_logs(session) session.install(".") - session.install("-r", "requirements/requirements.txt") - session.install("-r", "requirements/requirements-test.txt") - session.run("coverage", "run", "-p", "-m", "pytest", TESTS_PATH) + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") + + coverage = partial(session.run, "python", "-m", "coverage") + + coverage("erase") + + if "partial-coverage" in session.posargs: + coverage("run", "--parallel-mode", "--module", "pytest", TESTS_PATH) + else: + coverage("run", "--module", "pytest", TESTS_PATH) + coverage("report", "--show-missing", f"--fail-under={COVERAGE_FAIL_UNDER}") + coverage("json") -@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) +@nox.session() def coverage_combine(session: nox.Session) -> None: - """Combine all coverage partial files and generate JSON report.""" + """CI: Combine parallel-mode coverage files and produce reports.""" print_standard_logs(session) - fail_under = f"--fail-under={COVERAGE_FAIL_UNDER}" + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") - session.install(".") - session.install("-r", "requirements/requirements.txt") - session.install("-r", "requirements/requirements-test.txt") - session.run("python", "-m", "coverage", "combine") - session.run("python", "-m", "coverage", "report", "-m", fail_under) - session.run("python", "-m", "coverage", "json") + coverage = partial(session.run, "python", "-m", "coverage") + coverage("combine") + coverage("report", "--show-missing", f"--fail-under={COVERAGE_FAIL_UNDER}") + coverage("json") -@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) +@nox.session(python=DEFAULT_PYTHON) def mypy(session: nox.Session) -> None: """Run mypy against package and all required dependencies.""" print_standard_logs(session) @@ -110,15 +115,7 @@ def mypy(session: nox.Session) -> None: session.run("mypy", "-p", MODULE_NAME, "--no-incremental") -@nox.session(python=False, venv_backend=VENV_BACKEND) -def coverage(session: nox.Session) -> None: - """Generate a coverage report. Does not use a venv.""" - session.run("coverage", "erase") - session.run("coverage", "run", "-m", "pytest", TESTS_PATH) - session.run("coverage", "report", "-m") - - -@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND) +@nox.session(python=DEFAULT_PYTHON) def build(session: nox.Session) -> None: """Build distribution files.""" print_standard_logs(session) @@ -127,7 +124,7 @@ def build(session: nox.Session) -> None: session.run("python", "-m", "build") -@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="update-deps") +@nox.session(python=DEFAULT_PYTHON, name="update-deps") def update_deps(session: nox.Session) -> None: """Process requirement*.txt files, updating only additions/removals.""" print_standard_logs(session) @@ -145,7 +142,7 @@ def update_deps(session: nox.Session) -> None: ) -@nox.session(python=DEFAULT_PYTHON, venv_backend=VENV_BACKEND, name="upgrade-deps") +@nox.session(python=DEFAULT_PYTHON, name="upgrade-deps") def upgrade_deps(session: nox.Session) -> None: """Process requirement*.txt files and upgrade all libraries as possible.""" print_standard_logs(session) @@ -164,7 +161,7 @@ def upgrade_deps(session: nox.Session) -> None: ) -@nox.session(python=False, venv_backend=VENV_BACKEND) +@nox.session(python=False) def clean(_: nox.Session) -> None: """Clean cache, .pyc, .pyo, and test/build artifact files from project.""" count = 0 From 30409921ea89ac9d4344bac36156ceb6c3654346 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 03:06:18 -0400 Subject: [PATCH 19/28] Create lint session for nox (#190) * Create lint session for nox Move black, flake8, and isort actions into the `noxfile.py` alongside the existing mypy session. This will give the dev control over when these checks are run, allowing pre-commit to be optional in the local environment. There is a speed drawback as pre-commit runs on edited files only and nox will run on all file. The pre-commit config will remain and is always an option locally and in pre-commit.ci. * Update CI to use nox lint session * Remove black config * Add verbose flag to isort run --- .github/workflows/python-tests.yml | 10 ++++---- noxfile.py | 38 ++++++++++++++++++++---------- pyproject.toml | 4 ---- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 4aeed51..6bfe283 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -87,8 +87,8 @@ jobs: echo "TOTAL=$TOTAL" >> $GITHUB_ENV echo "### Total coverage: ${TOTAL}%" >> $GITHUB_STEP_SUMMARY - mypy-check: - name: "mypy strict enforcement" + linting: + name: "Check linting and formatting requirements" runs-on: "ubuntu-latest" steps: - name: "Repo checkout" @@ -101,8 +101,8 @@ jobs: - name: "Install nox" run: | - python -m pip install --upgrade pip nox + python -m pip install --upgrade nox - - name: "Enforce strict type annotations with mypy" + - name: "Run formatters and linters" run: | - nox --session mypy + nox --session lint diff --git a/noxfile.py b/noxfile.py index 658df4c..85c5963 100644 --- a/noxfile.py +++ b/noxfile.py @@ -13,7 +13,8 @@ TESTS_PATH = "tests" COVERAGE_FAIL_UNDER = 50 DEFAULT_PYTHON = "3.12" -VENV_PATH = ".venv" +VENV_PATH = "./.venv" +LINT_PATH = "./src" REQUIREMENTS_PATH = "./requirements" # What we allowed to clean (delete) @@ -33,10 +34,7 @@ # Define the default sessions run when `nox` is called on the CLI nox.options.default_venv_backend = "virtualenv" -nox.options.sessions = [ - "test", - "mypy", -] +nox.options.sessions = ["lint", "test"] @nox.session(python=False) @@ -104,15 +102,31 @@ def coverage_combine(session: nox.Session) -> None: coverage("json") -@nox.session(python=DEFAULT_PYTHON) -def mypy(session: nox.Session) -> None: - """Run mypy against package and all required dependencies.""" +@nox.session(name="lint") +def run_linters_and_formatters(session: nox.Session) -> None: + """Run code formatters, linters, and type checking against all files.""" print_standard_logs(session) - session.install(".") - session.install("-r", "requirements/requirements.txt") - session.install("-r", "requirements/requirements-dev.txt") - session.run("mypy", "-p", MODULE_NAME, "--no-incremental") + session.install(".", "-r", f"{REQUIREMENTS_PATH}/requirements-dev.txt") + + python = partial(session.run, "python", "-m") + + # Handle anything tool that applies corrections first. + python( + "isort", + "--verbose", + "--force-single-line-imports", + "--profile", + "black", + "--add-import", + "from __future__ import annotations", + LINT_PATH, + ) + python("black", "--verbose", LINT_PATH) + + # Linters: aka, things that yell but want you to fix things + python("flake8", "--show-source", "--verbose", LINT_PATH) + python("mypy", "--no-incremental", "--package", MODULE_NAME) @nox.session(python=DEFAULT_PYTHON) diff --git a/pyproject.toml b/pyproject.toml index 2ac4a93..c5a51de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,10 +30,6 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" [tool.setuptools_scm] # Purposely left empty -[tool.black] -line-length = 100 -target-version = ['py39'] - [tool.setuptools.package-data] "module_name" = ["py.typed"] From aef7161e609f3aaeb01d5904617ce71b8b4a3320 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 03:11:32 -0400 Subject: [PATCH 20/28] Remove flake8 and mypy hooks from pre-commit (#191) These hooks cannot fix themselves which defeats the power of pre-commit both locally and in the ci. Both `flake8` and `mypy` checks are now handled by nox. They are available locally with `nox -s lint` and run in the CI as a requirable job. --- .pre-commit-config.yaml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e5de32..beabc46 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,19 +30,3 @@ repos: rev: 25.1.0 hooks: - id: black - - # Flake8 for linting, line-length adjusted to match Black default - - repo: https://github.com/PyCQA/flake8 - rev: 7.1.2 - hooks: - - id: flake8 - additional_dependencies: - - flake8-builtins - - flake8-pep585 - - pep8-naming - - # Type enforcement for Python - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 - hooks: - - id: mypy From dd585850e01cea0f7882bc82f760910e93cc9a9a Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 04:00:30 -0400 Subject: [PATCH 21/28] Add 3.14 to CI, set default to 3.12 (#192) --- .github/workflows/python-tests.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 6bfe283..a1d0293 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -14,8 +14,18 @@ on: - "main" jobs: + settings: + runs-on: "ubuntu-latest" + name: "Define workflow settings" + outputs: + default-python-version: "3.12" + steps: + - name: "Define settings" + run: "" + run-tests-and-coverage: name: "Run pytest with coverage." + needs: ["settings"] runs-on: "${{ matrix.os }}" strategy: fail-fast: false @@ -30,6 +40,7 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" steps: - name: "Repo checkout" @@ -59,7 +70,7 @@ jobs: coverage-compile: name: "Compile coverage reports." - needs: "run-tests-and-coverage" + needs: ["settings", "run-tests-and-coverage"] runs-on: "ubuntu-latest" steps: - name: "Repo checkout" @@ -68,7 +79,7 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: - python-version: "3.12" + python-version: "${{ needs.settings.outputs.default-python-version }}" - name: "Install nox" run: | @@ -89,6 +100,7 @@ jobs: linting: name: "Check linting and formatting requirements" + needs: ["settings"] runs-on: "ubuntu-latest" steps: - name: "Repo checkout" @@ -97,7 +109,7 @@ jobs: - name: "Set up Python" uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" with: - python-version: "3.12" + python-version: "${{ needs.settings.outputs.default-python-version }}" - name: "Install nox" run: | From 1e052c9a704d13772eae31b5fa984a15587aed31 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 04:50:28 -0400 Subject: [PATCH 22/28] Clenaup and housekeeping (#193) * Remove setuptools_scm from template This is less one-size-fits-all than a targeted solution * Remove hardcoded python version defaults Use whatever nox was installed with * Clean up extra install call * Update documentation --- CONTRIBUTING.md | 19 ++++++------------- README.md | 8 +------- noxfile.py | 28 ++++++++++++---------------- pyproject.toml | 7 ++----- 4 files changed, 21 insertions(+), 41 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3ddec50..684db34 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,8 +25,7 @@ All pull requests must: Follow the patterns seen in the code. Walk where others have walked. -The majority of code style nits will be met when passing `pre-commit` checks -prior to submitting a pull request. +Be sure to run the expected formatters and linters prior to opening the PR. ### Tests @@ -70,22 +69,22 @@ package, and installs all dependency files. nox -s dev ``` -### Run tests with coverage (quick) +### Run tests with coverage ```console -nox -e coverage +nox -s test ``` -### Run tests (slow) +### Run formatters and linters ```console -nox +nox -s lint ``` ### Build dist ```console -nox -e build +nox -s build ``` --- @@ -119,9 +118,3 @@ nox -s upgrade-deps This repo is setup with a `.pre-commit-config.yaml` with the expectation that any code submitted for review already passes all selected pre-commit checks. - ---- - -## Error: File "setup.py" not found - -Update `pip` to at least version 22.3.1 diff --git a/README.md b/README.md index 3915202..52ce775 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,7 @@ fit. - **Q:** Why not put the requirements into the `pyproject.toml`? - **A:** Mostly because `pip-compile` does all the work for me and doesn't - target the `pyproject.toml`. Partly because many of my projects need to be - scanned by utilities that still think `requirements.txt` is the only pattern - to use. + target the `pyproject.toml` dependency groups yet. - **Q:** Why does this template change so often? - **A:** I'm constantly finding new tweaks that make the template fit just a @@ -39,7 +37,3 @@ fit. - **Q:** Have I heard of uv? - **A:** Yes. I'm already exploring a uv driven workflow for this template. - You can see it on the `uv-workflow` branch. I will be waiting until the - April 2025 release of pip for support of - [dependency-groups](https://packaging.python.org/en/latest/specifications/dependency-groups/) - before committing to any changes. diff --git a/noxfile.py b/noxfile.py index 85c5963..edca70c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -12,7 +12,6 @@ MODULE_NAME = "module_name" TESTS_PATH = "tests" COVERAGE_FAIL_UNDER = 50 -DEFAULT_PYTHON = "3.12" VENV_PATH = "./.venv" LINT_PATH = "./src" REQUIREMENTS_PATH = "./requirements" @@ -37,33 +36,31 @@ nox.options.sessions = ["lint", "test"] -@nox.session(python=False) +@nox.session() def dev(session: nox.Session) -> None: """Setup a development environment by creating the venv and installs dependencies.""" # Use the active environement if it exists, otherwise create a new one venv_path = os.environ.get("VIRTUAL_ENV", VENV_PATH) if sys.platform == "win32": - py_command = "py" venv_path = f"{venv_path}/Scripts" activate_command = f"{venv_path}/activate" else: - py_command = f"python{DEFAULT_PYTHON}" venv_path = f"{venv_path}/bin" activate_command = f"source {venv_path}/activate" if not os.path.exists(VENV_PATH): - session.run(py_command, "-m", "venv", VENV_PATH, "--upgrade-deps") + session.run("python", "-m", "venv", VENV_PATH, "--upgrade-deps") - python = f"{venv_path}/python" - requirement_files = get_requirement_files() + python = partial(session.run, f"{venv_path}/python", "-m") - session.run(python, "-m", "pip", "install", "-e", ".") + requirement_files = get_requirement_files() for requirement_file in requirement_files: - session.run(python, "-m", "pip", "install", "-r", requirement_file) + python("pip", "install", "-r", requirement_file, external=True) + python("pip", "install", "--editable", ".", external=True) - session.run(python, "-m", "pip", "install", "pre-commit") - session.run(f"{venv_path}/pre-commit", "install") + python("pip", "install", "pre-commit", external=True) + session.run(f"{venv_path}/pre-commit", "install", external=True) if not os.environ.get("VIRTUAL_ENV"): session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") @@ -74,8 +71,7 @@ def run_tests_with_coverage(session: nox.Session) -> None: """Run pytest with coverage, outputs console report and json.""" print_standard_logs(session) - session.install(".") - session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") + session.install(".", "-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") coverage = partial(session.run, "python", "-m", "coverage") @@ -129,7 +125,7 @@ def run_linters_and_formatters(session: nox.Session) -> None: python("mypy", "--no-incremental", "--package", MODULE_NAME) -@nox.session(python=DEFAULT_PYTHON) +@nox.session() def build(session: nox.Session) -> None: """Build distribution files.""" print_standard_logs(session) @@ -138,7 +134,7 @@ def build(session: nox.Session) -> None: session.run("python", "-m", "build") -@nox.session(python=DEFAULT_PYTHON, name="update-deps") +@nox.session(name="update-deps") def update_deps(session: nox.Session) -> None: """Process requirement*.txt files, updating only additions/removals.""" print_standard_logs(session) @@ -156,7 +152,7 @@ def update_deps(session: nox.Session) -> None: ) -@nox.session(python=DEFAULT_PYTHON, name="upgrade-deps") +@nox.session(name="upgrade-deps") def upgrade_deps(session: nox.Session) -> None: """Process requirement*.txt files and upgrade all libraries as possible.""" print_standard_logs(session) diff --git a/pyproject.toml b/pyproject.toml index c5a51de..b710fb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ [build-system] -requires = ["setuptools>=64", "setuptools_scm>=8"] +requires = ["setuptools>=64"] build-backend = "setuptools.build_meta" [project] name = "module-name" requires-python = ">=3.9" +version = "0.1.0" description = "Module Description" readme = "README.md" license = { file = "LICENSE" } @@ -19,7 +20,6 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", ] -dynamic = ["version"] [project.urls] homepage = "https://github.com/[ORG NAME]/[REPO NAME]" @@ -27,9 +27,6 @@ homepage = "https://github.com/[ORG NAME]/[REPO NAME]" # [project.scripts] # python-src-example = "module_name.sample:main" -[tool.setuptools_scm] -# Purposely left empty - [tool.setuptools.package-data] "module_name" = ["py.typed"] From 5b7ab1d05c8076698ad3777824ffff419920edcb Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 18:55:46 -0400 Subject: [PATCH 23/28] A case study... (#194) * Use a third-party dependency in the sample A case study of "test it if you expect it to work". * Add types-requests for mypy * Install requirements.txt as expected The conclusion of the case study. --- noxfile.py | 8 ++++++-- requirements/requirements-dev.txt | 1 + src/module_name/sample.py | 7 +++++++ tests/{test_sample.py => sample_test.py} | 8 ++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) rename tests/{test_sample.py => sample_test.py} (68%) diff --git a/noxfile.py b/noxfile.py index edca70c..885d5bc 100644 --- a/noxfile.py +++ b/noxfile.py @@ -71,7 +71,9 @@ def run_tests_with_coverage(session: nox.Session) -> None: """Run pytest with coverage, outputs console report and json.""" print_standard_logs(session) - session.install(".", "-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") + session.install(".") + session.install("-r", f"{REQUIREMENTS_PATH}/requirements.txt") + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") coverage = partial(session.run, "python", "-m", "coverage") @@ -103,7 +105,9 @@ def run_linters_and_formatters(session: nox.Session) -> None: """Run code formatters, linters, and type checking against all files.""" print_standard_logs(session) - session.install(".", "-r", f"{REQUIREMENTS_PATH}/requirements-dev.txt") + session.install(".") + session.install("-r", f"{REQUIREMENTS_PATH}/requirements.txt") + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-dev.txt") python = partial(session.run, "python", "-m") diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index cabf4c1..2c27647 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -12,3 +12,4 @@ isort # type checking mypy +types-requests diff --git a/src/module_name/sample.py b/src/module_name/sample.py index 8f0589b..1e90047 100644 --- a/src/module_name/sample.py +++ b/src/module_name/sample.py @@ -2,6 +2,8 @@ from __future__ import annotations +import requests + def main() -> bool: """Main""" @@ -17,3 +19,8 @@ def squared(value: int) -> int: def isodd(value: int) -> bool: """Return if value is odd.""" return bool(value % 2) + + +def health_check() -> bool: + """Returns true when github.com is accessible.""" + return requests.get("https://github.com").ok diff --git a/tests/test_sample.py b/tests/sample_test.py similarity index 68% rename from tests/test_sample.py rename to tests/sample_test.py index c748611..cfd2293 100644 --- a/tests/test_sample.py +++ b/tests/sample_test.py @@ -7,7 +7,7 @@ from module_name import sample -def test_main(): +def test_main() -> None: """Main test""" assert sample.main() @@ -20,5 +20,9 @@ def test_main(): (16, 256), ), ) -def test_squared(value_in, expected): +def test_squared(value_in: int, expected: int) -> None: assert sample.squared(value_in) == expected + + +def test_health_check() -> None: + assert sample.health_check() From 493ce5b210e5d0e613ef89e28f55a1d53e31a7e7 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 20:57:38 -0400 Subject: [PATCH 24/28] Hello dynamic dependencies, my old friends (#195) * Remove constraint line from requirement files * Add dynamic links to requirement files * Use contraint file when installing dependencies --- noxfile.py | 34 +++++++++++++++--------------- pyproject.toml | 10 ++++++++- requirements/constraints.txt | 5 +++-- requirements/requirements-dev.txt | 1 - requirements/requirements-test.txt | 1 - requirements/requirements.txt | 1 - 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/noxfile.py b/noxfile.py index 885d5bc..def8658 100644 --- a/noxfile.py +++ b/noxfile.py @@ -53,11 +53,12 @@ def dev(session: nox.Session) -> None: session.run("python", "-m", "venv", VENV_PATH, "--upgrade-deps") python = partial(session.run, f"{venv_path}/python", "-m") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") - requirement_files = get_requirement_files() - for requirement_file in requirement_files: - python("pip", "install", "-r", requirement_file, external=True) - python("pip", "install", "--editable", ".", external=True) + for requirement_file in get_requirement_files(): + python("pip", "install", "-r", requirement_file, *contraint, external=True) + + python("pip", "install", "--editable", ".", *contraint, external=True) python("pip", "install", "pre-commit", external=True) session.run(f"{venv_path}/pre-commit", "install", external=True) @@ -71,9 +72,9 @@ def run_tests_with_coverage(session: nox.Session) -> None: """Run pytest with coverage, outputs console report and json.""" print_standard_logs(session) - session.install(".") - session.install("-r", f"{REQUIREMENTS_PATH}/requirements.txt") - session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + + session.install(".[test]", *contraint) coverage = partial(session.run, "python", "-m", "coverage") @@ -92,7 +93,9 @@ def coverage_combine(session: nox.Session) -> None: """CI: Combine parallel-mode coverage files and produce reports.""" print_standard_logs(session) - session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt", *contraint) coverage = partial(session.run, "python", "-m", "coverage") coverage("combine") @@ -105,9 +108,8 @@ def run_linters_and_formatters(session: nox.Session) -> None: """Run code formatters, linters, and type checking against all files.""" print_standard_logs(session) - session.install(".") - session.install("-r", f"{REQUIREMENTS_PATH}/requirements.txt") - session.install("-r", f"{REQUIREMENTS_PATH}/requirements-dev.txt") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + session.install(".[dev]", *contraint) python = partial(session.run, "python", "-m") @@ -143,16 +145,15 @@ def update_deps(session: nox.Session) -> None: """Process requirement*.txt files, updating only additions/removals.""" print_standard_logs(session) - requirement_files = get_requirement_files() - session.install("pip-tools") session.run( "pip-compile", + "--strip-extras", "--no-annotate", "--no-emit-index-url", "--output-file", f"{REQUIREMENTS_PATH}/constraints.txt", - *requirement_files, + *get_requirement_files(), ) @@ -161,17 +162,16 @@ def upgrade_deps(session: nox.Session) -> None: """Process requirement*.txt files and upgrade all libraries as possible.""" print_standard_logs(session) - requirement_files = get_requirement_files() - session.install("pip-tools") session.run( "pip-compile", + "--strip-extras", "--no-annotate", "--no-emit-index-url", "--upgrade", "--output-file", f"{REQUIREMENTS_PATH}/constraints.txt", - *requirement_files, + *get_requirement_files(), ) diff --git a/pyproject.toml b/pyproject.toml index b710fb9..9ec8035 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=64"] +requires = [ "setuptools>=64" ] build-backend = "setuptools.build_meta" [project] @@ -20,6 +20,14 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", ] +dynamic = [ "dependencies", "optional-dependencies" ] + +[tool.setuptools.dynamic.dependencies] +file = [ "requirements/requirements.txt" ] + +[tool.setuptools.dynamic.optional-dependencies] +dev = { file = [ "requirements/requirements-dev.txt" ] } +test = { file = [ "requirements/requirements-test.txt" ] } [project.urls] homepage = "https://github.com/[ORG NAME]/[REPO NAME]" diff --git a/requirements/constraints.txt b/requirements/constraints.txt index ad74fee..938ee4b 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.12 # by the following command: # -# pip-compile --no-annotate --no-emit-index-url --output-file=./requirements/constraints.txt requirements/requirements-dev.txt requirements/requirements-test.txt requirements/requirements.txt +# pip-compile --no-annotate --no-emit-index-url --output-file=./requirements/constraints.txt --strip-extras requirements/requirements-dev.txt requirements/requirements-test.txt requirements/requirements.txt # black==25.1.0 certifi==2025.4.26 @@ -16,7 +16,7 @@ idna==3.10 iniconfig==2.1.0 isort==6.0.1 mccabe==0.7.0 -mypy==1.15.0 +mypy==1.16.0 mypy-extensions==1.1.0 packaging==25.0 pathspec==0.12.1 @@ -27,5 +27,6 @@ pyflakes==3.3.2 pytest==8.3.5 pytest-randomly==3.16.0 requests==2.32.3 +types-requests==2.32.0.20250515 typing-extensions==4.13.2 urllib3==2.4.0 diff --git a/requirements/requirements-dev.txt b/requirements/requirements-dev.txt index 2c27647..fc39ace 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,5 +1,4 @@ # Development Requirements - linting, formatting, etc. ---constraint ./constraints.txt # Linting flake8 diff --git a/requirements/requirements-test.txt b/requirements/requirements-test.txt index b01a13f..04472fe 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,5 +1,4 @@ # Testing Requirements ---constraint ./constraints.txt pytest pytest-randomly diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a0d3e12..2e95c63 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,3 @@ # Core Dependencies ---constraint ./constraints.txt requests From f8771b7d5e70b402d13c8ba72f067120fabc23c2 Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 30 May 2025 21:47:17 -0400 Subject: [PATCH 25/28] Update pyproject.license (#196) This does not change the license, only how it is listed. --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9ec8035..dc99d6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,14 +8,13 @@ requires-python = ">=3.9" version = "0.1.0" description = "Module Description" readme = "README.md" -license = { file = "LICENSE" } +license = "MIT" authors = [ { email = "yourname@email.invalid", name = "[YOUR NAME]" }, ] maintainers = [] keywords = [] classifiers = [ - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", From 2cf8ef8a38de68bff46a94f14f3714a30edfb895 Mon Sep 17 00:00:00 2001 From: Preocts Date: Sat, 31 May 2025 12:30:20 -0400 Subject: [PATCH 26/28] Update documents (#197) --- CONTRIBUTING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 684db34..2658fb3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ The following steps outline how to install this repo for local development. ### Clone repo -```console +```bash git clone https://github.com/[ORG NAME]/[REPO NAME] cd [REPO NAME] @@ -71,19 +71,19 @@ nox -s dev ### Run tests with coverage -```console +```bash nox -s test ``` ### Run formatters and linters -```console +```bash nox -s lint ``` ### Build dist -```console +```bash nox -s build ``` @@ -100,13 +100,13 @@ Once updated following the steps below, the package can be installed if needed. ### Update the generated files with changes -```console +```bash nox -s update-deps ``` ### Upgrade all generated dependencies -```console +```bash nox -s upgrade-deps ``` From 9a8db5472cb02c0803aaae05ff92978920162f4b Mon Sep 17 00:00:00 2001 From: Preocts Date: Sat, 31 May 2025 21:26:39 -0400 Subject: [PATCH 27/28] Update pinned versions for actions (#198) --- .github/workflows/python-tests.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index a1d0293..c004374 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -1,9 +1,9 @@ name: "python tests and coverage" # Uses: -# https://github.com/actions/setup-python : 0b93645e9fea7318ecaed2b359559ac225c90a2b +# https://github.com/actions/setup-python : a26af69be951a213d495a4c3e4e4022e16d87065 # https://github.com/actions/checkout : 11bd71901bbe5b1630ceea73d27597364c9af683 -# https://github.com/actions/download-artifact : fa0a91b85d4f404e444e00e005971372dc801d16 -# https://github.com/actions/upload-artifact : 6f51ac03b9356f520e9adb1b1b7802705f340c2b +# https://github.com/actions/download-artifact : d3f86a106a0bac45b974a628896c90dbdf5c8093 +# https://github.com/actions/upload-artifact : ea165f8d65b6e75b540449e92b4886f43607fa02 on: pull_request: @@ -47,7 +47,7 @@ jobs: uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python ${{ matrix.python-version }}" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -61,7 +61,7 @@ jobs: nox --session test -- partial-coverage - name: "Save coverage artifact" - uses: "actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b" + uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" with: name: "coverage-artifact-${{ matrix.os}}-${{ matrix.python-version}}" path: ".coverage.*" @@ -77,7 +77,7 @@ jobs: uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" with: python-version: "${{ needs.settings.outputs.default-python-version }}" @@ -86,7 +86,7 @@ jobs: python -m pip install --upgrade nox - name: "Download coverage artifacts" - uses: "actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16" + uses: "actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093" with: pattern: "coverage-artifact-*" merge-multiple: true @@ -107,7 +107,7 @@ jobs: uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Set up Python" - uses: "actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b" + uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" with: python-version: "${{ needs.settings.outputs.default-python-version }}" From 58366b9a12ab102d315bcdd0d8b329aebad521af Mon Sep 17 00:00:00 2001 From: Preocts Date: Fri, 6 Jun 2025 10:28:23 -0400 Subject: [PATCH 28/28] Include ./tests in nox lint session (#200) * Simplify install steps * Handle running lint session on tests/ --- noxfile.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/noxfile.py b/noxfile.py index def8658..8b3b34f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,6 +16,25 @@ LINT_PATH = "./src" REQUIREMENTS_PATH = "./requirements" + +LINTING_COMMANDS = ( + ( + "isort", + "--verbose", + "--force-single-line-imports", + "--profile", + "black", + "--add-import", + "from __future__ import annotations", + LINT_PATH, + TESTS_PATH, + ), + ("black", "--verbose", LINT_PATH, TESTS_PATH), + ("flake8", "--show-source", "--verbose", LINT_PATH, TESTS_PATH), + ("mypy", "--no-incremental", "--package", MODULE_NAME), + ("mypy", "--no-incremental", TESTS_PATH), +) + # What we allowed to clean (delete) CLEANABLE_TARGETS = [ "./dist", @@ -55,10 +74,7 @@ def dev(session: nox.Session) -> None: python = partial(session.run, f"{venv_path}/python", "-m") contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") - for requirement_file in get_requirement_files(): - python("pip", "install", "-r", requirement_file, *contraint, external=True) - - python("pip", "install", "--editable", ".", *contraint, external=True) + python("pip", "install", "--editable", ".[dev,test]", *contraint, external=True) python("pip", "install", "pre-commit", external=True) session.run(f"{venv_path}/pre-commit", "install", external=True) @@ -109,26 +125,12 @@ def run_linters_and_formatters(session: nox.Session) -> None: print_standard_logs(session) contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") - session.install(".[dev]", *contraint) + session.install(".[dev,test]", *contraint) python = partial(session.run, "python", "-m") - # Handle anything tool that applies corrections first. - python( - "isort", - "--verbose", - "--force-single-line-imports", - "--profile", - "black", - "--add-import", - "from __future__ import annotations", - LINT_PATH, - ) - python("black", "--verbose", LINT_PATH) - - # Linters: aka, things that yell but want you to fix things - python("flake8", "--show-source", "--verbose", LINT_PATH) - python("mypy", "--no-incremental", "--package", MODULE_NAME) + for linter_command in LINTING_COMMANDS: + python(*linter_command) @nox.session()