Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Changes here will be overwritten by Copier
_commit: v0.0.2
_commit: v0.0.4
_src_path: ../template-python/
author: Kevin Weiss
author_email: weiss.kevin604@gmail.com
Expand Down
68 changes: 68 additions & 0 deletions .github/agents/pr-review.agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
description: Review PR commits against master — check architecture, bugs, test coverage and commit messages, apply fixup commits, then propose a PR title and summary.
---

You are a thorough PR reviewer for Lobaro Python projects. Work through the
steps below in order. Be specific about problems and their locations. Do not
skip a step even if the previous one passed cleanly.

## Step 1 — Map the branch

```
git log master..HEAD --oneline
```

List every commit on the branch. Then for each commit, run `git show <sha>` to
read its diff in full before forming any opinion.

## Step 2 — Review each commit

For each commit evaluate the four areas below. Collect all findings before
making any changes.

**Architecture / intent**
Does the change make sense at the design level? Is the approach sound? Does it
fit the project structure and direction?

**Bugs**
Any logic errors, off-by-ones, unhandled edge-cases, thread-safety issues, or
resource leaks? Would the code fail for any valid input?

**Test coverage**
Are the changed code paths exercised by tests? Are edge-cases and error paths
covered? New behaviour without tests is a finding.

**Commit message**
Must be a valid [Conventional Commit](https://www.conventionalcommits.org/):
`type(scope): description` — lower-case description, no trailing period.
Common types: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `build`.
Check that the type, scope, and description accurately reflect the diff.

Present a summary of all findings before moving on.

## Step 3 — Fix problems one by one

For each finding, in order:

1. Apply the fix to the source files.
2. Run `tox -e lint` — fix any issues and repeat until it passes cleanly.
3. Run `tox` — fix any failures and repeat until it passes cleanly.
4. Create the smallest accurate commit:
- If the fix belongs squarely to an existing commit: `git commit --fixup=<sha>`
- If it is genuinely new work (e.g. additional tests, a separate bug):
use a new conventional commit message.

**Never amend or rebase the user's existing commits. Only add new commits.**

Iterate through all findings until all are resolved.

## Step 4 — Propose a PR summary

Once all findings are addressed, output:

1. The updated commit list: `git log master..HEAD --oneline`
2. A recommended PR title in conventional commit format.
3. A short PR body (3–8 sentences) explaining what changed, why, and any
migration or review notes.

Pause here and wait for the user to review each commit before they squash.
15 changes: 15 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# GitHub Copilot Instructions — helpers

<!-- This file was generated once by copier (Lobaro Python template) and is yours to extend.
VS Code also picks up .github/instructions/lobaro-template.instructions.md automatically,
which defines project conventions (layout, patterns, workflow, CLI).
JetBrains only reads this file — sync from lobaro-template.instructions.md after `copier update`. -->

Simple python based helpers for lobaro tools.
This is a Lobaro Python library (`lob-hlpr`), module `lob_hlpr`.

Instructions generic information based off the
[Lobaro Python template](https://github.com/lobaro/template-python)
(layout, patterns, workflow, CLI) is defined in
[lobaro-template.instructions.md](.github/instructions/lobaro-template.instructions.md).
<!-- Add project-specific context below, e.g. domain concepts, key dependencies, architecture notes. -->
40 changes: 40 additions & 0 deletions .github/instructions/lobaro-template.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
applyTo: "**"
---
# Lobaro Project Instructions

## Project layout

- Source: `src/lob_hlpr/` — all library/app code lives here
- Tests: `tests/`
- Config: `pyproject.toml`, if configs can be in different files, prefer that (e.g. `tox.ini`, `pytest.ini`)
- Versioning: `setuptools-scm` — version comes from git tags, never hardcode it
- Dependencies: loose version bounds in `pyproject.toml`; pinned snapshots in `requirements*.txt`

## Python version

Python ≥ 3.10. Always use built-in generics — `list[X]`, `dict[K, V]`, `X | None` — never `typing.List` etc.

## Validation workflow

Always run lint first, fix all issues, then run the full test suite:

```
tox -e lint # check and fix all style issues
tox # full test suite across Python versions
```

If lint passes but tests fail, diagnose the test failure — do not bypass checks.

## Code patterns

- Use `dataclasses.dataclass` for data containers — never Pydantic (not a dependency)
- Use `logging` in every module — never `print` in library code
- Prefer named functions over lambdas
- Follow PEP 8 and PEP 257
- Type-hint all public functions and methods
- Try not to add third-party dependencies unless necessary — prefer built-in features

## Agents

- PR review agent: [instructions](../agents/pr-review.agent.md)
53 changes: 33 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,48 @@ concurrency:
cancel-in-progress: true

env:
TOX_VERSION: "4.24.1"
TOX_VERSION: "4.49.1"
TOX_UV_VERSION: "1.33.4"
UV_VERSION: "0.10.11"
MIN_PYTHON_VERSION: "3.10"
MAX_PYTHON_VERSION: "3.12"
MAX_PYTHON_VERSION: "3.13"

jobs:
check-commits:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # deep clone for setuptools-scm
- name: Prevent fixup/squash/WIP commits
run: |
BASE_SHA=$(git merge-base origin/main HEAD 2>/dev/null || git merge-base origin/master HEAD)
git log $BASE_SHA..HEAD --pretty=%s | grep -E '^(fixup!|squash!|WIP|wip)' && exit 1 || echo "✅ ok"

prepare:
runs-on: ubuntu-24.04
outputs:
wheel-distribution: ${{ steps.wheel-distribution.outputs.path }}
VERSION: ${{ steps.get_version.outputs.VERSION }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
fetch-depth: 0 # deep clone for setuptools-scm
- uses: actions/setup-python@v5
id: setup-python
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
python-version: ${{ env.MIN_PYTHON_VERSION }}
- name: Run static analysis and format checkers
run: >-
pipx run
--python '${{ steps.setup-python.outputs.python-path }}'
--spec tox==${{ env.TOX_VERSION }}
uv tool run
--from "tox==${{ env.TOX_VERSION }}"
--with "tox-uv==${{ env.TOX_UV_VERSION }}"
tox -e lint
- name: Build package distribution files
run: >-
pipx run
--python '${{ steps.setup-python.outputs.python-path }}'
--spec tox==${{ env.TOX_VERSION }}
uv tool run
--from "tox==${{ env.TOX_VERSION }}"
--with "tox-uv==${{ env.TOX_UV_VERSION }}"
tox -e clean,build
- name: Record the path of wheel distribution
id: wheel-distribution
Expand All @@ -59,7 +72,7 @@ jobs:
- name: Store the distribution files for use in other stages
# `tests` and `publish` will use the same pre-built distributions,
# so we make sure to release the exact same package that was tested
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: python-distribution-files
path: dist/
Expand All @@ -71,26 +84,26 @@ jobs:
matrix:
python:
- "3.10"
- "3.12"
- "3.13"
platform:
- ubuntu-24.04
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
id: setup-python
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
python-version: ${{ matrix.python }}
- name: Retrieve pre-built distribution files
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: python-distribution-files
path: dist/
- name: Run tests
run: >-
pipx run
--python '${{ steps.setup-python.outputs.python-path }}'
--spec tox==${{ env.TOX_VERSION }}
uv tool run
--from "tox==${{ env.TOX_VERSION }}"
--with "tox-uv==${{ env.TOX_UV_VERSION }}"
tox --installpkg '${{ needs.prepare.outputs.wheel-distribution }}'
-- -rFEx --durations 10 --color yes

Expand Down
70 changes: 42 additions & 28 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,48 +1,62 @@
exclude: '^docs/conf.py'

default_install_hook_types: [pre-commit, commit-msg]

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: "v6.0.0"
hooks:
- id: check-added-large-files
args: ['--maxkb=16384']
- id: check-ast
- id: check-case-conflict
- id: check-illegal-windows-names
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-xml
- id: check-yaml
- id: debug-statements
- id: destroyed-symlinks
- id: end-of-file-fixer
- id: pretty-format-json
args: ['--autofix']
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: trailing-whitespace
- id: check-added-large-files
args: ['--maxkb=16384']
- id: check-ast
- id: check-case-conflict
- id: check-illegal-windows-names
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-xml
- id: check-yaml
- id: debug-statements
- id: destroyed-symlinks
- id: end-of-file-fixer
- id: pretty-format-json
args: ['--autofix']
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: trailing-whitespace

- repo: https://github.com/tox-dev/pyproject-fmt
rev: "v2.5.0"
rev: "v2.18.1"
hooks:
- id: pyproject-fmt

- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: "v2.4.2"
hooks:
- id: codespell
- id: codespell
stages: [commit-msg, pre-commit]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.3
rev: "v0.15.6"
hooks:
# Run the linter.
- id: ruff
- id: ruff-check
# Run the formatter.
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: 'v1.14.1'
- repo: https://github.com/DetachHead/basedpyright-prek-mirror
rev: "1.38.2"
hooks:
- id: basedpyright

- repo: https://github.com/gitleaks/gitleaks
rev: "v8.24.2"
hooks:
- id: gitleaks

- repo: https://github.com/compilerla/conventional-pre-commit
rev: "v4.4.0"
hooks:
- id: mypy
- id: conventional-pre-commit
stages: [commit-msg]
25 changes: 10 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
[build-system]
build-backend = "setuptools.build_meta"

requires = [
"setuptools>=64",
"setuptools-scm[toml]>=8",
"setuptools>=77",
"setuptools-scm[toml]>=9",
]

[project]
name = "lob-hlpr"
description = "Simple python based helpers for lobaro tools."
readme = "README.md"
license = { file = "LICENSE" }
license-files = [ "LICENSE" ]
authors = [
{ name = "Kevin Weiss", email = "weiss.kevin604@gmail.com" },
]
Expand All @@ -22,32 +21,28 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dynamic = [ "version" ]

dependencies = [
#
]

optional-dependencies.testing = [
"pytest~=8.3.4",
"pytest-cov~=6.0.0",
"pytest~=9.0.2",
"pytest-cov~=7.0.0",
]

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = [ "src" ]

[tool.setuptools.package-data]
lob_hlpr = [
package-data.lob_hlpr = [
"py.typed",
]
packages.find.where = [ "src" ]

[tool.setuptools_scm]
# For smarter version schemes and other configuration options,
# check out https://github.com/pypa/setuptools_scm
version_scheme = "no-guess-dev"

[tool.pyproject-fmt]
max_supported_python = "3.12"
max_supported_python = "3.13"
11 changes: 11 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"reportImplicitOverride": "none",
"reportImplicitStringConcatenation": "none",
"reportMissingImports": "none",
"reportMissingModuleSource": "none",
"reportMissingTypeArgument": "none",
"reportOptionalMemberAccess": "none",
"reportUnannotatedClassAttribute": "none",
"reportUnusedCallResult": "none",
"typeCheckingMode": "basic"
}
Loading