initial commit

This commit is contained in:
2025-10-07 09:55:18 -05:00
commit 11fb0d773b
18 changed files with 1230 additions and 0 deletions

3
.gitattributes vendored Normal file
View File

@@ -0,0 +1,3 @@
* text=auto
*.py text diff=python

73
.gitea/setup-secrets.sh Normal file
View File

@@ -0,0 +1,73 @@
#!/bin/bash
# This script helps set up the required secrets for Gitea Actions
# Run this script locally to upload secrets to your Gitea instance
# Also claude wrote this script, use it more as a reference and base for hacking than anything actually useful.
set -e
# Ensure token is provided
if [ -z "$GITEA_TOKEN" ]; then
echo "Error: GITEA_TOKEN environment variable must be set"
echo "Create a token at https://gitea.deepak.science/user/settings/applications"
echo "Then run: export GITEA_TOKEN=your_token"
exit 1
fi
# API URL from your Gitea instance
GITEA_API_URL="https://gitea.deepak.science/api/v1"
REPO_OWNER="$(git remote get-url origin | sed -E 's/.*[:/]([^/]+)\/[^/]+$/\1/')"
REPO_NAME="$(git remote get-url origin | sed -E 's/.*[:/][^/]+\/([^/.]+)(\.git)?$/\1/')"
echo "Setting up secrets for $REPO_OWNER/$REPO_NAME"
# Function to create/update a secret
create_secret() {
local secret_name="$1"
local secret_value="$2"
local description="$3"
echo "Setting up secret: $secret_name - $description"
# Create payload
local payload="{\"name\":\"$secret_name\",\"data\":\"$secret_value\"}"
# Send to Gitea API
curl -X PUT \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload" \
"$GITEA_API_URL/repos/$REPO_OWNER/$REPO_NAME/secrets/$secret_name"
echo -e "\nDone!\n"
}
# Prompt for required secrets
read -r -p "Enter SSH_GITEA_SSH_KEY (private SSH key for Git operations): " -s SSH_KEY
echo
create_secret "SSH_GITEA_SSH_KEY" "$SSH_KEY" "SSH key for Git operations in CI"
read -r -p "Enter SSH_GITEA_KNOWN_HOSTS (content for known_hosts file): " KNOWN_HOSTS
create_secret "SSH_GITEA_KNOWN_HOSTS" "$KNOWN_HOSTS" "Known hosts for SSH connections"
read -r -p "Enter PYPI_USERNAME: " PYPI_USER
create_secret "PYPI_USERNAME" "$PYPI_USER" "PyPI username for publishing"
read -r -p "Enter PYPI_API_TOKEN: " -s PYPI_TOKEN
echo
create_secret "PYPI_API_TOKEN" "$PYPI_TOKEN" "PyPI API token for publishing"
read -r -p "Enter ATTIC_ENDPOINT (URL for Attic cache): " ATTIC_ENDPOINT
create_secret "ATTIC_ENDPOINT" "$ATTIC_ENDPOINT" "Attic cache endpoint URL"
read -r -p "Enter ATTIC_CACHE (name of Attic cache): " ATTIC_CACHE
create_secret "ATTIC_CACHE" "$ATTIC_CACHE" "Attic cache name"
read -r -p "Enter ATTIC_TOKEN (authentication token for Attic): " -s ATTIC_TOKEN
echo
create_secret "ATTIC_TOKEN" "$ATTIC_TOKEN" "Attic authentication token"
# Create GITEA_TOKEN secret for self-service PR creation
create_secret "GITEA_TOKEN" "$GITEA_TOKEN" "Gitea API token for automation"
echo "All secrets have been set up! Your Gitea Actions workflows should now work properly."
echo "Make sure to give execution permission to this script with: chmod +x .gitea/setup-secrets.sh"

View File

@@ -0,0 +1,78 @@
name: Nix Tests
run-name: ${{ gitea.actor }} running Nix tests
on:
push:
branches: ["*"]
pull_request:
branches: [master]
workflow_dispatch:
jobs:
nix-test:
# Using matrix to run on different runners if needed
strategy:
fail-fast: true
matrix:
# You can adjust to run on different runners or environments
# os: [ubuntu-latest, nix-runner]
os: [nix-runner]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# SSH keys setup is important for accessing private repositories
# Adjust or remove this section if not needed
# - name: Install SSH keys
# uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4
# with:
# key: ${{ secrets.SSH_GITEA_SSH_KEY }}
# name: gitea_action
# known_hosts: ${{ secrets.SSH_GITEA_KNOWN_HOSTS }}
# - name: Agent SSH keys
# uses: webfactory/ssh-agent@v0.9.0
# with:
# ssh-private-key: ${{ secrets.SSH_GITEA_SSH_KEY }}
# Cache nix https://github.com/cachix/install-nix-action/issues/56#issuecomment-1240991760
- name: "Cache Nix store"
uses: actions/cache@v4
id: nix-cache
with:
path: /tmp/nixcache
key: nix-d-${{ runner.os }}-HELLOCHANGEME-${{ hashFiles('**/pyproject.toml', '**/*.nix', '**/uv.lock', '**/scripts/*.sh', '**/flake.lock') }}
restore-keys: |
nix-d-${{ runner.os }}-HELLOCHANGEME-
nix-d-${{ runner.os }}
nix-d
# Installing Nix - not necessary if using nix-runner but included for completeness
# - name: Install Nix
# uses: cachix/install-nix-action@v31
- name: Setup Attic Cache
uses: ryanccn/attic-action@3354ae812cb672e1381be4c7914204c44db53866
with:
endpoint: ${{ secrets.ATTIC_ENDPOINT }}
cache: ${{ secrets.ATTIC_CACHE }}
token: ${{ secrets.ATTIC_TOKEN }}
- name: "Import Nix store cache"
continue-on-error: true
# if: "steps.nix-cache.outputs.cache-hit == 'true'"
run: bash scripts/restore_cache.sh
# Build and check the Nix flake
- name: Build and Check Nix Flake
run: nix flake check
# Artifacts collection
- name: List files after test
run: ls
- name: Archive test results
uses: christopherhx/gitea-upload-artifact@v4
if: always() # Always upload test results, even if tests fail
with:
name: test-results-nix
path: |
pytest.xml
coverage.xml
htmlcov/
retention-days: 7
- name: "Export Nix store cache"
# if: "steps.nix-cache.outputs.cache-hit != 'true'"
run: bash scripts/populate_cache.sh

View File

@@ -0,0 +1,139 @@
name: Build and Publish
on:
push:
# Only trigger on tags with version format
tags:
- '*.*.*'
# Allow manual triggering for testing
workflow_dispatch:
jobs:
# Use Python build first, as fallback
build-python:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install build tools
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build package
run: |
python -m build
- name: Store Python build
uses: christopherhx/gitea-upload-artifact@v4
with:
name: python-package
path: |
dist/*.tar.gz
dist/*.whl
retention-days: 7
build-uv:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install the project
run: uv sync --all-extras --dev
- name: Build package
run: |
uv build
- name: Store Python build
uses: christopherhx/gitea-upload-artifact@v4
with:
name: uv-package
path: |
dist/*.tar.gz
dist/*.whl
retention-days: 7
# Use Nix build as primary method
# build-nix:
# runs-on: nix-runner
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - name: Setup Attic Cache
# uses: ryanccn/attic-action@3354ae812cb672e1381be4c7914204c44db53866
# with:
# endpoint: ${{ secrets.ATTIC_ENDPOINT }}
# cache: ${{ secrets.ATTIC_CACHE }}
# token: ${{ secrets.ATTIC_TOKEN }}
# # Build the package using Nix
# - name: Build with Nix
# run: |
# # Build Python package with Nix
# # Adjust this to match your flake output for the Python package
# nix build .#pythonPackage
# - name: Copy built package to dist
# run: |
# mkdir -p dist
# cp -r result/* dist/ || echo "Failed to copy result to dist - check paths"
# - name: Store Nix build
# uses: christopherhx/gitea-upload-artifact@v4
# with:
# name: nix-package
# path: dist/
# retention-days: 7
# Publish to PyPI
publish-pypi:
needs: [build-python, build-uv]
runs-on: ubuntu-latest
environment:
name: pypi
# Only publish on tag push, not on manual workflow dispatch
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Download Uv build
uses: christopherhx/gitea-download-artifact@v4
with:
name: uv-package
path: dist-uv
- name: Download Python build (fallback)
uses: christopherhx/gitea-download-artifact@v4
with:
name: python-package
path: dist-python
- name: Setup distribution directory
run: |
mkdir -p dist
# Prefer Nix build results, fall back to Python build
if [ -n "$(ls -A dist-uv 2>/dev/null)" ]; then
echo "Using uv build artifacts"
cp -r dist-uv/* dist/
elif [ -n "$(ls -A dist-nix 2>/dev/null)" ]; then
echo "Using Nix build artifacts"
cp -r dist-nix/* dist/
else
echo "Using Python build artifacts"
cp -r dist-python/* dist/
fi
- name: Print distribution directory
run: ls -la dist
- name: Publish to PyPI
run: uv publish
env:
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
# Use TestPyPI for non-production releases
# repository-url: https://test.pypi.org/legacy/

View File

@@ -0,0 +1,66 @@
name: Python Tests
run-name: ${{ gitea.actor }} running Python tests
on:
push:
branches: ["*"]
pull_request:
branches: [master]
workflow_dispatch:
jobs:
python-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: "Set up Python"
uses: actions/setup-python@v5
with:
python-version-file: "pyproject.toml"
- name: Install dependencies
run: |
uv sync --all-extras --dev
# This check ensures formatting is correct without modifying files
- name: Check formatting
run: |
uv run ruff format --check src tests
- name: Run linting
run: |
uv run ruff check src tests
- name: Run type checking
run: |
uv run mypy src
- name: Run tests
run: |
uv run pytest
# Artifacts collection
- name: Archive test results
uses: christopherhx/gitea-upload-artifact@v4
if: always() # Always upload test results, even if tests fail
with:
name: test-results
path: |
pytest.xml
coverage.xml
htmlcov/
retention-days: 7
# This lets you see the coverage report in the Gitea UI
- name: Comment coverage
# Only run on PRs
if: github.event_name == 'pull_request'
run: |
COVERAGE=$(python -c "import xml.etree.ElementTree as ET; tree = ET.parse('coverage.xml'); root = tree.getroot(); print(float(root.attrib['line-rate']) * 100)")
echo "Current coverage: ${COVERAGE}%"
# Post comment to PR using Gitea API
curl -X POST \
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
-H "Content-Type: application/json" \
-d "{\"body\": \"📊 **Test Coverage**: ${COVERAGE}%\"}" \
${{ gitea.api_url }}/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments

156
.gitignore vendored Normal file
View File

@@ -0,0 +1,156 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
pytest.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# direnv
.envrc
.direnv
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# *.csv
local_scripts/
.vscode
logs/
out/
*.xlsx
# nix
result

10
.versionrc Normal file
View File

@@ -0,0 +1,10 @@
{
"bumpFiles": [
{
"filename": "pyproject.toml",
"type": "python"
}
],
"sign": true,
"tag-prefix": ""
}

37
README.md Normal file
View File

@@ -0,0 +1,37 @@
# hello-world - The default project (change me).
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg?style=flat-square)](https://conventionalcommits.org)
[![PyPI](https://img.shields.io/pypi/v/{{ NAME }}?style=flat-square)](https://pypi.org/project/{{ NAME }}/)
![Maintenance](https://img.shields.io/maintenance/yes/2025?style=flat-square)
Default to using justfile as local script holder
Testing out using uv
Using poetry for management, etc.
In nix, nix fmt does formatting for everything.
# Getting started
1. You may need to initialise a git repository.
* `git init`, `git add .`, `git commit -m "initial commit"`
2. Double check if you need to update nixpkgs, update python version etc.
* in nix, some version of `nix flake update`, changing python version in flake.nix etc.
3. The placeholder names used throughout are not all consistent at all.
Sorry.
Iteratively bug slaying should get you there.
4. Get a dev shell to play around.
Probably allowing direnv will be the easiest startup.
Remember that it won't work until you fix all the placeholder names and make an initial commit.
So
5. Do that.
## dev
Build with `just`, preferred over `do.sh` I think.
## CLI
Details of how to use this cli in this project

162
flake.nix Normal file
View File

@@ -0,0 +1,162 @@
{
description = "Application packaged using poetry2nix";
inputs.nixpkgs.url = "github:NixOS/nixpkgs";
inputs.treefmt-nix.url = "github:numtide/treefmt-nix";
inputs.pyproject-nix.url = "github:pyproject-nix/pyproject.nix";
inputs.pyproject-nix.inputs.nixpkgs.follows = "nixpkgs";
inputs.uv2nix = {
url = "github:pyproject-nix/uv2nix";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
inputs.pyproject-build-systems = {
url = "github:pyproject-nix/build-system-pkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.uv2nix.follows = "uv2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
self,
nixpkgs,
treefmt-nix,
uv2nix,
pyproject-nix,
pyproject-build-systems,
}:
let
# inherit (nixpkgs) lib;
# pkgs = nixpkgs.legacyPackages.${system};
supportedSystems = [ "x86_64-linux" ];
pkgsFor =
system:
nixpkgs.legacyPackages.${system}.extend (
# blank but overlays can go here
nixpkgs.lib.composeManyExtensions ([ ])
);
eachSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f (pkgsFor system));
treefmtEval = eachSystem (pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix);
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
overlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; };
pythonSet = (
pkgs:
let
python = pkgs.python312;
pyprojectOverrides = final: prev: {
jaconv = prev.jaconv.overrideAttrs (old: {
nativeBuildInputs =
old.nativeBuildInputs or [ ]
++ (final.resolveBuildSystem {
setuptools-scm = [ ];
setuptools = [ ];
});
});
};
in
(pkgs.callPackage pyproject-nix.build.packages { inherit python; }).overrideScope (
nixpkgs.lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
pyprojectOverrides
]
)
);
in
{
# formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixpkgs-fmt);
formatter = eachSystem (pkgs: treefmtEval.${pkgs.system}.config.build.wrapper);
# for `nix flake check`
checks = eachSystem (pkgs: {
formatting = treefmtEval.${pkgs.system}.config.build.check self;
});
packages = eachSystem (pkgs: {
default = (pythonSet pkgs).mkVirtualEnv "hello-world-env" workspace.deps.default;
});
devShells = eachSystem (
pkgs:
let
inherit (pkgs) lib;
thisPythonSet = pythonSet pkgs;
editableOverlay = workspace.mkEditablePyprojectOverlay { root = "$REPO_ROOT"; };
# Override previous set with our overrideable overlay.
editablePythonSet = thisPythonSet.overrideScope (
lib.composeManyExtensions [
editableOverlay
# Apply fixups for building an editable package of your workspace packages
(final: prev: {
# Change some stuff here for your new project!
hello-world = prev.hello-world.overrideAttrs (old: {
# It's a good idea to filter the sources going into an editable build
# so the editable package doesn't have to be rebuilt on every change.
# I stole this from pyproject-nix but still don't know what this does
src = lib.fileset.toSource {
root = old.src;
fileset = lib.fileset.unions [
(old.src + "/pyproject.toml")
(old.src + "/README.md")
(old.src + "/src/hello_world/__init__.py")
];
};
# Hatchling (our build system) has a dependency on the `editables` package when building editables.
#
# In normal Python flows this dependency is dynamically handled, and doesn't need to be explicitly declared.
# This behaviour is documented in PEP-660.
#
# With Nix the dependency needs to be explicitly declared.
nativeBuildInputs = old.nativeBuildInputs ++ final.resolveBuildSystem { editables = [ ]; };
});
})
]
);
virtualenv = editablePythonSet.mkVirtualEnv "hello-world-dev-env" workspace.deps.all;
in
{
default = pkgs.mkShell {
# inputsFrom = [ self.packages.${system}.myappApp ];
packages = [
virtualenv
pkgs.uv
pkgs.pyright
pkgs.nodejs
pkgs.just
];
env = {
DO_NIX_CUSTOM = "1";
# Force uv to use Python interpreter from venv
UV_PYTHON = "${virtualenv}/bin/python";
# Prevent uv from downloading managed Python's
UV_PYTHON_DOWNLOADS = "never";
};
shellHook = ''
# Undo dependency propagation by nixpkgs.
unset PYTHONPATH
# Get repository root using git. This is expanded at runtime by the editable `.pth` machinery.
export REPO_ROOT=$(git rev-parse --show-toplevel)
'';
};
}
);
};
}

67
justfile Normal file
View File

@@ -0,0 +1,67 @@
# List just commands by default
default:
just --list
# builds the python module using poetry
build:
echo "building..."
# poetry build
# print a message displaying whether nix is being used
checknix:
#!/usr/bin/env bash
set -euxo pipefail
if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then
echo "In an interactive nix env."
else
echo "Using uv as runner, no nix detected."
fi
# update all test snapshots, use if snapshots are out of date
update-snapshots:
#!/usr/bin/env bash
set -euxo pipefail
uv run pytest --snapshot-update
# run all tests
test:
#!/usr/bin/env bash
set -euxo pipefail
# would love test: fmt to make sure formatting happens but in WSL formatting is slow...
# poor filesystem access performance
echo "testing..."
uv run ruff check src tests
uv run mypy src
uv run pytest
check:
#!/usr/bin/env bash
set -euxo pipefail
nix flake check
# format code
fmt:
#!/usr/bin/env bash
set -euxo pipefail
# uv run ruff --format
nix fmt
# release the app, checking that our working tree is clean and ready for release
release:
./scripts/release.sh
htmlcov:
poetry run pytest --cov-report=html
zsh_completions:
#!/usr/bin/env bash
set -euxo pipefail
if [[ "${DO_NIX_CUSTOM:=0}" -eq 1 ]]; then
eval "$(_HELLO_WORLD_COMPLETE=zsh_source hello_world)"
else
echo "Nope only nix."
fi

67
pyproject.toml Normal file
View File

@@ -0,0 +1,67 @@
[project]
name = "hello-world"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
#"urllib3>=2.2.3",
]
[project.scripts]
hello = "hello_world:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"flake8>=7.1.2",
"mypy>=1.15.0",
"pytest>=8.3.5",
"pytest-cov>=6.0.0",
"ruff>=0.6.7",
"syrupy>=4.9.0",
]
[tool.ruff.format]
indent-style = "tab"
[tool.hatch.build.targets.wheel]
sources = ["src"]
# [tool.poetry.group.dev.dependencies]
# mypy = "*"
# python-semantic-release = "*"
[tool.pytest.ini_options]
testpaths = ["tests"]
# Uncomment to care about coverage
addopts = "--junitxml pytest.xml --cov src --cov-report=xml:coverage.xml --cov-fail-under=50 --cov-report=html"
junit_family = "xunit1"
log_format = "%(asctime)s | %(levelname)s | %(pathname)s:%(lineno)d | %(message)s"
log_level = "WARNING"
# [tool.mypy]
# If you need this
# plugins = "numpy.typing.mypy_plugin"
# [[tool.mypy.overrides]]
# module = [
# "scipy",
# "scipy.optimize",
# "scipy.stats",
# "scipy.fft",
# ]
# ignore_missing_imports = true
# [tool.semantic_release]
# branch = "master"
# version_variable = [
# "src/__init__.py:__version__",
# "pyproject.toml:version"
# ]
# upload_to_pypi = false
# upload_to_release = false
# build_command = "pip install poetry && poetry build"j

36
scripts/populate_cache.sh Normal file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -Eeuo pipefail
CACHE_PATH=${CACHE_PATH:-"/tmp/nixcache"}
banner() {
echo "========================================================"
echo " $*"
echo "========================================================"
}
banner "List what we start with"
nix-store --query --requisites --include-outputs "$(nix eval .\#devShells.x86_64-linux.default.drvPath --raw)" >dependencies.txt
nix-store --query --requisites --include-outputs "$(nix eval .\#packages.x86_64-linux.default.drvPath --raw)" >>dependencies.txt
# nix-store --query --requisites --include-outputs "$(nix eval .\#checks.x86_64-linux.formatting.drvPath --raw)" >>dependencies.txt
# nix-store --query --requisites --include-outputs "$(nix eval .\#checks.x86_64-linux.test-check.drvPath --raw)" >>dependencies.txt
nix-store --query --requisites --include-outputs "$(nix eval .\#formatter.x86_64-linux.drvPath --raw)" >>dependencies.txt
sort -o dependencies.txt -u dependencies.txt
banner "list obtained paths to cache"
wc -l dependencies.txt
banner "filter out our matches"
echo "Using filter"
cat scripts/populate_cache_exclude_patterns.txt
grep -vf scripts/populate_cache_exclude_patterns.txt dependencies.txt >filtered_dependencies.txt
echo "Count our filtered"
wc -l filtered_dependencies.txt
xargs <filtered_dependencies.txt -r nix copy --to "file://${CACHE_PATH}" --no-substitute

View File

@@ -0,0 +1,6 @@
ts-ensourcel
ts_ensourcel
syrupy
pytest-cov
nix-shell
pyproject

16
scripts/restore_cache.sh Normal file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -Eeuo pipefail
CACHE_PATH=${CACHE_PATH:-"/tmp/nixcache"}
banner() {
echo "========================================================"
echo " $*"
echo "========================================================"
}
# banner "List what we start with"
# ls -alh "${CACHE_PATH}"
banner "Copy time"
nix copy --from "file://${CACHE_PATH}" --no-check-sigs --all

View File

@@ -0,0 +1,22 @@
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
__version__ = "0.0.1"
def get_version():
return __version__
def welcome_message():
return "Hello"
def main():
print(welcome_message())
__all__ = [
"__version__",
"get_version",
]

11
tests/test_me.py Normal file
View File

@@ -0,0 +1,11 @@
from hello_world import __version__
import hello_world
def test_version():
assert hello_world.get_version() == __version__
def welcome_should_match(snapshot):
message = hello_world.welcome_message()
assert message == snapshot

27
treefmt.nix Normal file
View File

@@ -0,0 +1,27 @@
# treefmt.nix
{ ... }:
{
projectRootFile = "treefmt.nix";
settings.global.excludes = [
"*.toml"
"Jenkinsfile"
"*.txt"
".gitattributes"
"CLAUDE.md"
".parrot.md"
"*.ambr"
".python-version"
];
programs.deadnix.enable = true;
programs.mdsh.enable = true;
programs.nixfmt.enable = true;
programs.shellcheck.enable = true;
programs.shfmt.enable = true;
programs.yamlfmt.enable = true;
programs.just.enable = true;
programs.ruff-format.enable = true;
programs.ruff-check.enable = true;
}

254
uv.lock generated Normal file
View File

@@ -0,0 +1,254 @@
version = 1
revision = 1
requires-python = ">=3.12"
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "coverage"
version = "7.6.12"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0c/d6/2b53ab3ee99f2262e6f0b8369a43f6d66658eab45510331c0b3d5c8c4272/coverage-7.6.12.tar.gz", hash = "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", size = 805941 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e2/7f/4af2ed1d06ce6bee7eafc03b2ef748b14132b0bdae04388e451e4b2c529b/coverage-7.6.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", size = 208645 },
{ url = "https://files.pythonhosted.org/packages/dc/60/d19df912989117caa95123524d26fc973f56dc14aecdec5ccd7d0084e131/coverage-7.6.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", size = 208898 },
{ url = "https://files.pythonhosted.org/packages/bd/10/fecabcf438ba676f706bf90186ccf6ff9f6158cc494286965c76e58742fa/coverage-7.6.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", size = 242987 },
{ url = "https://files.pythonhosted.org/packages/4c/53/4e208440389e8ea936f5f2b0762dcd4cb03281a7722def8e2bf9dc9c3d68/coverage-7.6.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", size = 239881 },
{ url = "https://files.pythonhosted.org/packages/c4/47/2ba744af8d2f0caa1f17e7746147e34dfc5f811fb65fc153153722d58835/coverage-7.6.12-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", size = 242142 },
{ url = "https://files.pythonhosted.org/packages/e9/90/df726af8ee74d92ee7e3bf113bf101ea4315d71508952bd21abc3fae471e/coverage-7.6.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", size = 241437 },
{ url = "https://files.pythonhosted.org/packages/f6/af/995263fd04ae5f9cf12521150295bf03b6ba940d0aea97953bb4a6db3e2b/coverage-7.6.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", size = 239724 },
{ url = "https://files.pythonhosted.org/packages/1c/8e/5bb04f0318805e190984c6ce106b4c3968a9562a400180e549855d8211bd/coverage-7.6.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", size = 241329 },
{ url = "https://files.pythonhosted.org/packages/9e/9d/fa04d9e6c3f6459f4e0b231925277cfc33d72dfab7fa19c312c03e59da99/coverage-7.6.12-cp312-cp312-win32.whl", hash = "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", size = 211289 },
{ url = "https://files.pythonhosted.org/packages/53/40/53c7ffe3c0c3fff4d708bc99e65f3d78c129110d6629736faf2dbd60ad57/coverage-7.6.12-cp312-cp312-win_amd64.whl", hash = "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", size = 212079 },
{ url = "https://files.pythonhosted.org/packages/76/89/1adf3e634753c0de3dad2f02aac1e73dba58bc5a3a914ac94a25b2ef418f/coverage-7.6.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", size = 208673 },
{ url = "https://files.pythonhosted.org/packages/ce/64/92a4e239d64d798535c5b45baac6b891c205a8a2e7c9cc8590ad386693dc/coverage-7.6.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", size = 208945 },
{ url = "https://files.pythonhosted.org/packages/b4/d0/4596a3ef3bca20a94539c9b1e10fd250225d1dec57ea78b0867a1cf9742e/coverage-7.6.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", size = 242484 },
{ url = "https://files.pythonhosted.org/packages/1c/ef/6fd0d344695af6718a38d0861408af48a709327335486a7ad7e85936dc6e/coverage-7.6.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", size = 239525 },
{ url = "https://files.pythonhosted.org/packages/0c/4b/373be2be7dd42f2bcd6964059fd8fa307d265a29d2b9bcf1d044bcc156ed/coverage-7.6.12-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", size = 241545 },
{ url = "https://files.pythonhosted.org/packages/a6/7d/0e83cc2673a7790650851ee92f72a343827ecaaea07960587c8f442b5cd3/coverage-7.6.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", size = 241179 },
{ url = "https://files.pythonhosted.org/packages/ff/8c/566ea92ce2bb7627b0900124e24a99f9244b6c8c92d09ff9f7633eb7c3c8/coverage-7.6.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", size = 239288 },
{ url = "https://files.pythonhosted.org/packages/7d/e4/869a138e50b622f796782d642c15fb5f25a5870c6d0059a663667a201638/coverage-7.6.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", size = 241032 },
{ url = "https://files.pythonhosted.org/packages/ae/28/a52ff5d62a9f9e9fe9c4f17759b98632edd3a3489fce70154c7d66054dd3/coverage-7.6.12-cp313-cp313-win32.whl", hash = "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", size = 211315 },
{ url = "https://files.pythonhosted.org/packages/bc/17/ab849b7429a639f9722fa5628364c28d675c7ff37ebc3268fe9840dda13c/coverage-7.6.12-cp313-cp313-win_amd64.whl", hash = "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", size = 212099 },
{ url = "https://files.pythonhosted.org/packages/d2/1c/b9965bf23e171d98505eb5eb4fb4d05c44efd256f2e0f19ad1ba8c3f54b0/coverage-7.6.12-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", size = 209511 },
{ url = "https://files.pythonhosted.org/packages/57/b3/119c201d3b692d5e17784fee876a9a78e1b3051327de2709392962877ca8/coverage-7.6.12-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", size = 209729 },
{ url = "https://files.pythonhosted.org/packages/52/4e/a7feb5a56b266304bc59f872ea07b728e14d5a64f1ad3a2cc01a3259c965/coverage-7.6.12-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", size = 253988 },
{ url = "https://files.pythonhosted.org/packages/65/19/069fec4d6908d0dae98126aa7ad08ce5130a6decc8509da7740d36e8e8d2/coverage-7.6.12-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", size = 249697 },
{ url = "https://files.pythonhosted.org/packages/1c/da/5b19f09ba39df7c55f77820736bf17bbe2416bbf5216a3100ac019e15839/coverage-7.6.12-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", size = 252033 },
{ url = "https://files.pythonhosted.org/packages/1e/89/4c2750df7f80a7872267f7c5fe497c69d45f688f7b3afe1297e52e33f791/coverage-7.6.12-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", size = 251535 },
{ url = "https://files.pythonhosted.org/packages/78/3b/6d3ae3c1cc05f1b0460c51e6f6dcf567598cbd7c6121e5ad06643974703c/coverage-7.6.12-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", size = 249192 },
{ url = "https://files.pythonhosted.org/packages/6e/8e/c14a79f535ce41af7d436bbad0d3d90c43d9e38ec409b4770c894031422e/coverage-7.6.12-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", size = 250627 },
{ url = "https://files.pythonhosted.org/packages/cb/79/b7cee656cfb17a7f2c1b9c3cee03dd5d8000ca299ad4038ba64b61a9b044/coverage-7.6.12-cp313-cp313t-win32.whl", hash = "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", size = 212033 },
{ url = "https://files.pythonhosted.org/packages/b6/c3/f7aaa3813f1fa9a4228175a7bd368199659d392897e184435a3b66408dd3/coverage-7.6.12-cp313-cp313t-win_amd64.whl", hash = "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", size = 213240 },
{ url = "https://files.pythonhosted.org/packages/fb/b2/f655700e1024dec98b10ebaafd0cedbc25e40e4abe62a3c8e2ceef4f8f0a/coverage-7.6.12-py3-none-any.whl", hash = "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", size = 200552 },
]
[[package]]
name = "flake8"
version = "7.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mccabe" },
{ name = "pycodestyle" },
{ name = "pyflakes" },
]
sdist = { url = "https://files.pythonhosted.org/packages/58/16/3f2a0bb700ad65ac9663262905a025917c020a3f92f014d2ba8964b4602c/flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd", size = 48119 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/35/f8/08d37b2cd89da306e3520bd27f8a85692122b42b56c0c2c3784ff09c022f/flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a", size = 57745 },
]
[[package]]
name = "hello-world"
version = "0.1.0"
source = { editable = "." }
[package.dev-dependencies]
dev = [
{ name = "flake8" },
{ name = "mypy" },
{ name = "pytest" },
{ name = "pytest-cov" },
{ name = "ruff" },
{ name = "syrupy" },
]
[package.metadata]
[package.metadata.requires-dev]
dev = [
{ name = "flake8", specifier = ">=7.1.2" },
{ name = "mypy", specifier = ">=1.15.0" },
{ name = "pytest", specifier = ">=8.3.5" },
{ name = "pytest-cov", specifier = ">=6.0.0" },
{ name = "ruff", specifier = ">=0.6.7" },
{ name = "syrupy", specifier = ">=4.9.0" },
]
[[package]]
name = "iniconfig"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
]
[[package]]
name = "mccabe"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 },
]
[[package]]
name = "mypy"
version = "1.15.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 },
{ url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 },
{ url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 },
{ url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 },
{ url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 },
{ url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 },
{ url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 },
{ url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 },
{ url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 },
{ url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 },
{ url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 },
{ url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 },
{ url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 },
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
]
[[package]]
name = "packaging"
version = "24.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
]
[[package]]
name = "pluggy"
version = "1.5.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
]
[[package]]
name = "pycodestyle"
version = "2.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284 },
]
[[package]]
name = "pyflakes"
version = "3.2.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 },
]
[[package]]
name = "pytest"
version = "8.3.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "iniconfig" },
{ name = "packaging" },
{ name = "pluggy" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
]
[[package]]
name = "pytest-cov"
version = "6.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "coverage" },
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
]
[[package]]
name = "ruff"
version = "0.9.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/20/8e/fafaa6f15c332e73425d9c44ada85360501045d5ab0b81400076aff27cf6/ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7", size = 3759776 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/73/b2/af7c2cc9e438cbc19fafeec4f20bfcd72165460fe75b2b6e9a0958c8c62b/ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d", size = 10049494 },
{ url = "https://files.pythonhosted.org/packages/6d/12/03f6dfa1b95ddd47e6969f0225d60d9d7437c91938a310835feb27927ca0/ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d", size = 10853584 },
{ url = "https://files.pythonhosted.org/packages/02/49/1c79e0906b6ff551fb0894168763f705bf980864739572b2815ecd3c9df0/ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d", size = 10155692 },
{ url = "https://files.pythonhosted.org/packages/5b/01/85e8082e41585e0e1ceb11e41c054e9e36fed45f4b210991052d8a75089f/ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c", size = 10369760 },
{ url = "https://files.pythonhosted.org/packages/a1/90/0bc60bd4e5db051f12445046d0c85cc2c617095c0904f1aa81067dc64aea/ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e", size = 9912196 },
{ url = "https://files.pythonhosted.org/packages/66/ea/0b7e8c42b1ec608033c4d5a02939c82097ddcb0b3e393e4238584b7054ab/ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12", size = 11434985 },
{ url = "https://files.pythonhosted.org/packages/d5/86/3171d1eff893db4f91755175a6e1163c5887be1f1e2f4f6c0c59527c2bfd/ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16", size = 12155842 },
{ url = "https://files.pythonhosted.org/packages/89/9e/700ca289f172a38eb0bca752056d0a42637fa17b81649b9331786cb791d7/ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52", size = 11613804 },
{ url = "https://files.pythonhosted.org/packages/f2/92/648020b3b5db180f41a931a68b1c8575cca3e63cec86fd26807422a0dbad/ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1", size = 13823776 },
{ url = "https://files.pythonhosted.org/packages/5e/a6/cc472161cd04d30a09d5c90698696b70c169eeba2c41030344194242db45/ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c", size = 11302673 },
{ url = "https://files.pythonhosted.org/packages/6c/db/d31c361c4025b1b9102b4d032c70a69adb9ee6fde093f6c3bf29f831c85c/ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43", size = 10235358 },
{ url = "https://files.pythonhosted.org/packages/d1/86/d6374e24a14d4d93ebe120f45edd82ad7dcf3ef999ffc92b197d81cdc2a5/ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c", size = 9886177 },
{ url = "https://files.pythonhosted.org/packages/00/62/a61691f6eaaac1e945a1f3f59f1eea9a218513139d5b6c2b8f88b43b5b8f/ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5", size = 10864747 },
{ url = "https://files.pythonhosted.org/packages/ee/94/2c7065e1d92a8a8a46d46d9c3cf07b0aa7e0a1e0153d74baa5e6620b4102/ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8", size = 11360441 },
{ url = "https://files.pythonhosted.org/packages/a7/8f/1f545ea6f9fcd7bf4368551fb91d2064d8f0577b3079bb3f0ae5779fb773/ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029", size = 10247401 },
{ url = "https://files.pythonhosted.org/packages/4f/18/fb703603ab108e5c165f52f5b86ee2aa9be43bb781703ec87c66a5f5d604/ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1", size = 11366360 },
{ url = "https://files.pythonhosted.org/packages/35/85/338e603dc68e7d9994d5d84f24adbf69bae760ba5efd3e20f5ff2cec18da/ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69", size = 10436892 },
]
[[package]]
name = "syrupy"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pytest" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8f/47/821aad6be7a3ee5ef590369dab1ba9f969aad0ca1d8e92ef28d737d056ba/syrupy-4.9.0.tar.gz", hash = "sha256:7bd822492a6fa9f6f98832c2ca24f513b3e69a5690f4b2166bdb3b3941eef767", size = 52304 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/78/1e7aee4498f79624f85a6480eb8bc04bacb729c30336bc61b384c6ce4998/syrupy-4.9.0-py3-none-any.whl", hash = "sha256:3028d60188df9b39079678501be7d72fe64d32e2bb53d78df87f5a84bde94d76", size = 52045 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]