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..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 -# https://github.com/actions/checkout -# https://github.com/actions/download-artifact -# https://github.com/actions/upload-artifact +# https://github.com/actions/setup-python : a26af69be951a213d495a4c3e4e4022e16d87065 +# https://github.com/actions/checkout : 11bd71901bbe5b1630ceea73d27597364c9af683 +# https://github.com/actions/download-artifact : d3f86a106a0bac45b974a628896c90dbdf5c8093 +# https://github.com/actions/upload-artifact : ea165f8d65b6e75b540449e92b4886f43607fa02 on: pull_request: @@ -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 nox for tests and coverage" + name: "Run pytest with coverage." + needs: ["settings"] runs-on: "${{ matrix.os }}" strategy: fail-fast: false @@ -30,78 +40,81 @@ jobs: - "3.11" - "3.12" - "3.13" + - "3.14" 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@a26af69be951a213d495a4c3e4e4022e16d87065" with: python-version: "${{ matrix.python-version }}" allow-prereleases: true - 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 tests_with_coverage-${{ matrix.python-version }} + nox --session test -- partial-coverage - name: "Save coverage artifact" - uses: "actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3" + uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" with: name: "coverage-artifact-${{ matrix.os}}-${{ matrix.python-version}}" path: ".coverage.*" retention-days: 1 + include-hidden-files: true coverage-compile: - name: "coverage compile" - needs: "run-tests-and-coverage" + name: "Compile coverage reports." + needs: ["settings", "run-tests-and-coverage"] 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@a26af69be951a213d495a4c3e4e4022e16d87065" with: - python-version: "3.12" + python-version: "${{ needs.settings.outputs.default-python-version }}" - 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@c850b930e6ba138125429b7e5c93fc707a7f8427" + uses: "actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093" with: pattern: "coverage-artifact-*" merge-multiple: true - 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 - mypy-check: - name: "mypy strict enforcement" + linting: + name: "Check linting and formatting requirements" + needs: ["settings"] 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@a26af69be951a213d495a4c3e4e4022e16d87065" with: - python-version: "3.12" + python-version: "${{ needs.settings.outputs.default-python-version }}" - 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_check + nox --session lint 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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d41ed7..beabc46 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,12 +12,10 @@ 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 - rev: 5.13.2 + rev: 6.0.1 hooks: - id: isort args: @@ -29,22 +27,6 @@ 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: 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.0 - 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.10.1 - hooks: - - id: mypy diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb09367..2658fb3 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,100 @@ 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. +Be sure to run the expected formatters and linters prior to opening the PR. -## 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 + +```bash +git clone https://github.com/[ORG NAME]/[REPO NAME] + +cd [REPO NAME] +``` + +### [Install nox](https://nox.thea.codes/en/stable/index.html) + +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. + + +## Nox Sessions + +### Developer Install + +This builds the `/.venv`, installs the editable +package, and installs all dependency files. + +```bash +nox -s dev +``` + +### Run tests with coverage + +```bash +nox -s test +``` + +### Run formatters and linters + +```bash +nox -s lint +``` + +### Build dist + +```bash +nox -s build +``` + +--- + +## Updating dependencies + +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 `constraints.txt` file. + +Once updated following the steps below, the package can be installed if needed. + +### Update the generated files with changes + +```bash +nox -s update-deps +``` + +### Upgrade all generated dependencies + +```bash +nox -s upgrade-deps +``` + +--- + +## [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. diff --git a/LICENSE b/LICENSE index 0af0ae9..795f190 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 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 diff --git a/README.md b/README.md index 8861f43..52ce775 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. @@ -21,170 +26,14 @@ 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 - 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 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 +- **Q:** Have I heard of uv? + - **A:** Yes. I'm already exploring a uv driven workflow for this template. 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() diff --git a/noxfile.py b/noxfile.py index 1839451..8b3b34f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,6 +4,7 @@ import pathlib import shutil import sys +from functools import partial import nox @@ -11,14 +12,28 @@ MODULE_NAME = "module_name" TESTS_PATH = "tests" COVERAGE_FAIL_UNDER = 50 -DEFAULT_PYTHON_VERSION = "3.12" -PYTHON_MATRIX = ["3.9", "3.10", "3.11", "3.12", "3.13"] -VENV_PATH = "venv" -REQUIREMENT_IN_FILES = [ - pathlib.Path("requirements/requirements.in"), - pathlib.Path("requirements/requirements-dev.in"), - pathlib.Path("requirements/requirements-test.in"), -] +VENV_PATH = "./.venv" +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 = [ @@ -35,108 +50,131 @@ "./**/*.pyo", ] - # Define the default sessions run when `nox` is called on the CLI -nox.options.sessions = [ - "tests_with_coverage", - "coverage_combine_and_report", - "mypy_check", -] +nox.options.default_venv_backend = "virtualenv" +nox.options.sessions = ["lint", "test"] -@nox.session(python=PYTHON_MATRIX) -def tests_with_coverage(session: nox.Session) -> None: - """Run unit tests with coverage saved to partial file.""" - print_standard_logs(session) +@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) - session.install(".[test]") - session.run("coverage", "run", "-p", "-m", "pytest", TESTS_PATH) + if sys.platform == "win32": + venv_path = f"{venv_path}/Scripts" + activate_command = f"{venv_path}/activate" + else: + venv_path = f"{venv_path}/bin" + activate_command = f"source {venv_path}/activate" + if not os.path.exists(VENV_PATH): + session.run("python", "-m", "venv", VENV_PATH, "--upgrade-deps") -@nox.session(python=DEFAULT_PYTHON_VERSION) -def coverage_combine_and_report(session: nox.Session) -> None: - """Combine all coverage partial files and generate JSON report.""" - print_standard_logs(session) + python = partial(session.run, f"{venv_path}/python", "-m") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + + python("pip", "install", "--editable", ".[dev,test]", *contraint, external=True) - fail_under = f"--fail-under={COVERAGE_FAIL_UNDER}" + python("pip", "install", "pre-commit", external=True) + session.run(f"{venv_path}/pre-commit", "install", external=True) - session.install(".[test]") - session.run("python", "-m", "coverage", "combine") - session.run("python", "-m", "coverage", "report", "-m", fail_under) - session.run("python", "-m", "coverage", "json") + if not os.environ.get("VIRTUAL_ENV"): + session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") -@nox.session(python=DEFAULT_PYTHON_VERSION) -def mypy_check(session: nox.Session) -> None: - """Run mypy against package and all required dependencies.""" +@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("mypy") - session.run("mypy", "-p", MODULE_NAME, "--no-incremental") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + session.install(".[test]", *contraint) -@nox.session(python=False) -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") + coverage = partial(session.run, "python", "-m", "coverage") + coverage("erase") -@nox.session(python=DEFAULT_PYTHON_VERSION) -def build(session: nox.Session) -> None: - """Build distribution files.""" + 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() +def coverage_combine(session: nox.Session) -> None: + """CI: Combine parallel-mode coverage files and produce reports.""" print_standard_logs(session) - session.install("build") - session.run("python", "-m", "build") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + session.install("-r", f"{REQUIREMENTS_PATH}/requirements-test.txt", *contraint) -@nox.session(python=False) -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) + coverage = partial(session.run, "python", "-m", "coverage") + coverage("combine") + coverage("report", "--show-missing", f"--fail-under={COVERAGE_FAIL_UNDER}") + coverage("json") - 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) - session.run(f"{venv_path}/python", "-m", "pip", "install", "--upgrade", "pip") +@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.run(f"{venv_path}/python", "-m", "pip", "install", "-e", ".[dev,test]") - session.run(f"{venv_path}/pre-commit", "install") + contraint = ("--constraint", f"{REQUIREMENTS_PATH}/constraints.txt") + session.install(".[dev,test]", *contraint) - if not os.environ.get("VIRTUAL_ENV"): - session.log(f"\n\nRun '{activate_command}' to enter the virtual environment.\n") + python = partial(session.run, "python", "-m") + + for linter_command in LINTING_COMMANDS: + python(*linter_command) -@nox.session(python=DEFAULT_PYTHON_VERSION) -def update(session: nox.Session) -> None: - """Process requirement*.in files, updating only additions/removals.""" +@nox.session() +def build(session: nox.Session) -> None: + """Build distribution files.""" print_standard_logs(session) - session.install("pip-tools") - for filename in REQUIREMENT_IN_FILES: - session.run("pip-compile", "--no-emit-index-url", str(filename)) + session.install("build") + session.run("python", "-m", "build") + +@nox.session(name="update-deps") +def update_deps(session: nox.Session) -> None: + """Process requirement*.txt files, updating only additions/removals.""" + print_standard_logs(session) -@nox.session(python=DEFAULT_PYTHON_VERSION) -def upgrade(session: nox.Session) -> None: - """Process requirement*.in files and upgrade all libraries as possible.""" + session.install("pip-tools") + session.run( + "pip-compile", + "--strip-extras", + "--no-annotate", + "--no-emit-index-url", + "--output-file", + f"{REQUIREMENTS_PATH}/constraints.txt", + *get_requirement_files(), + ) + + +@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) 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", + "--strip-extras", + "--no-annotate", + "--no-emit-index-url", + "--upgrade", + "--output-file", + f"{REQUIREMENTS_PATH}/constraints.txt", + *get_requirement_files(), + ) @nox.session(python=False) @@ -159,3 +197,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 284d9dc..dc99d6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,40 +1,36 @@ [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" } +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", ] -dynamic = ["dependencies", "optional-dependencies", "version"] - -[tool.setuptools_scm] -# Purposely left empty +dynamic = [ "dependencies", "optional-dependencies" ] [tool.setuptools.dynamic.dependencies] -file = ["requirements/requirements.txt"] +file = [ "requirements/requirements.txt" ] [tool.setuptools.dynamic.optional-dependencies] -dev = { file = ["requirements/requirements-dev.txt"] } -test = { file = ["requirements/requirements-test.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" diff --git a/requirements/constraints.txt b/requirements/constraints.txt new file mode 100644 index 0000000..938ee4b --- /dev/null +++ b/requirements/constraints.txt @@ -0,0 +1,32 @@ +# +# 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 --strip-extras 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.16.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 +types-requests==2.32.0.20250515 +typing-extensions==4.13.2 +urllib3==2.4.0 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..fc39ace 100644 --- a/requirements/requirements-dev.txt +++ b/requirements/requirements-dev.txt @@ -1,62 +1,14 @@ -# -# 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 +# Linting +flake8 +flake8-builtins +flake8-pep585 + +# formatters +black +isort + +# type checking +mypy +types-requests 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..04472fe 100644 --- a/requirements/requirements-test.txt +++ b/requirements/requirements-test.txt @@ -1,36 +1,5 @@ -# -# 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 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..2e95c63 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,6 +1,3 @@ -# -# 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 + +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()