weightsdb 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- weightsdb-0.2.0/.editorconfig +23 -0
- weightsdb-0.2.0/.github/workflows/ci.yml +164 -0
- weightsdb-0.2.0/.github/workflows/release.yml +57 -0
- weightsdb-0.2.0/.gitignore +100 -0
- weightsdb-0.2.0/.importlinter +21 -0
- weightsdb-0.2.0/.pre-commit-config.yaml +22 -0
- weightsdb-0.2.0/CHANGELOG.md +75 -0
- weightsdb-0.2.0/CONTRIBUTING.md +50 -0
- weightsdb-0.2.0/LICENSE +201 -0
- weightsdb-0.2.0/PKG-INFO +96 -0
- weightsdb-0.2.0/README.md +62 -0
- weightsdb-0.2.0/SECURITY.md +39 -0
- weightsdb-0.2.0/docs/README.md +8 -0
- weightsdb-0.2.0/docs/adoption-checklist.md +132 -0
- weightsdb-0.2.0/docs/packages/weightsdb/development-plan.md +158 -0
- weightsdb-0.2.0/docs/packages/weightsdb/spec.md +265 -0
- weightsdb-0.2.0/docs/quickstart.md +146 -0
- weightsdb-0.2.0/pyproject.toml +104 -0
- weightsdb-0.2.0/requirements/README.md +64 -0
- weightsdb-0.2.0/requirements/ci.lock +848 -0
- weightsdb-0.2.0/requirements/release.in +6 -0
- weightsdb-0.2.0/requirements/release.lock +493 -0
- weightsdb-0.2.0/src/weightsdb/__about__.py +1 -0
- weightsdb-0.2.0/src/weightsdb/__init__.py +71 -0
- weightsdb-0.2.0/src/weightsdb/backup.py +610 -0
- weightsdb-0.2.0/src/weightsdb/engine.py +229 -0
- weightsdb-0.2.0/src/weightsdb/errors.py +83 -0
- weightsdb-0.2.0/src/weightsdb/health.py +318 -0
- weightsdb-0.2.0/src/weightsdb/migrations.py +328 -0
- weightsdb-0.2.0/src/weightsdb/py.typed +0 -0
- weightsdb-0.2.0/src/weightsdb/redaction.py +26 -0
- weightsdb-0.2.0/src/weightsdb/session.py +115 -0
- weightsdb-0.2.0/src/weightsdb/testing.py +131 -0
- weightsdb-0.2.0/src/weightsdb/types.py +187 -0
- weightsdb-0.2.0/tests/conftest.py +19 -0
- weightsdb-0.2.0/tests/contract/test_public_api.py +107 -0
- weightsdb-0.2.0/tests/integration/_migration_fixture/__init__.py +0 -0
- weightsdb-0.2.0/tests/integration/_migration_fixture/env.py +44 -0
- weightsdb-0.2.0/tests/integration/_migration_fixture/models.py +20 -0
- weightsdb-0.2.0/tests/integration/_migration_fixture/versions/0001_create_widgets.py +28 -0
- weightsdb-0.2.0/tests/integration/_migration_fixture/versions/0002_add_note.py +25 -0
- weightsdb-0.2.0/tests/integration/test_backup_restore.py +625 -0
- weightsdb-0.2.0/tests/integration/test_migrations.py +240 -0
- weightsdb-0.2.0/tests/integration/test_two_schemas.py +69 -0
- weightsdb-0.2.0/tests/performance/test_db_overhead.py +124 -0
- weightsdb-0.2.0/tests/unit/test_engine.py +273 -0
- weightsdb-0.2.0/tests/unit/test_health.py +331 -0
- weightsdb-0.2.0/tests/unit/test_redaction.py +35 -0
- weightsdb-0.2.0/tests/unit/test_session.py +98 -0
- weightsdb-0.2.0/tests/unit/test_testing.py +47 -0
- weightsdb-0.2.0/tests/unit/test_types.py +242 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.py]
|
|
12
|
+
indent_size = 4
|
|
13
|
+
max_line_length = 100
|
|
14
|
+
|
|
15
|
+
[*.{toml,yml,yaml,json}]
|
|
16
|
+
indent_size = 2
|
|
17
|
+
|
|
18
|
+
[*.md]
|
|
19
|
+
trim_trailing_whitespace = false
|
|
20
|
+
max_line_length = off
|
|
21
|
+
|
|
22
|
+
[Makefile]
|
|
23
|
+
indent_style = tab
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
format:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with: { python-version: "3.12" }
|
|
15
|
+
- run: pip install ruff
|
|
16
|
+
- run: ruff format --check .
|
|
17
|
+
|
|
18
|
+
lint:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-python@v5
|
|
23
|
+
with: { python-version: "3.12" }
|
|
24
|
+
- run: pip install ruff
|
|
25
|
+
- run: ruff check .
|
|
26
|
+
|
|
27
|
+
types:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: actions/setup-python@v5
|
|
32
|
+
with: { python-version: "3.12" }
|
|
33
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
34
|
+
- run: pip install . --no-deps
|
|
35
|
+
- run: mypy src tests
|
|
36
|
+
|
|
37
|
+
boundaries:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- uses: actions/setup-python@v5
|
|
42
|
+
with: { python-version: "3.12" }
|
|
43
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
44
|
+
- run: pip install . --no-deps
|
|
45
|
+
- run: lint-imports
|
|
46
|
+
|
|
47
|
+
tests:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
strategy:
|
|
50
|
+
matrix:
|
|
51
|
+
python-version: ["3.12", "3.13"]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: actions/setup-python@v5
|
|
55
|
+
with: { python-version: "${{ matrix.python-version }}" }
|
|
56
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
57
|
+
- run: pip install . --no-deps
|
|
58
|
+
- run: pytest -m "not live and not performance" --cov --cov-report=xml
|
|
59
|
+
|
|
60
|
+
tests-314-early-warning:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
continue-on-error: true
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
- uses: actions/setup-python@v5
|
|
66
|
+
with: { python-version: "3.14" }
|
|
67
|
+
# Deliberately unpinned: ci.lock is resolved on 3.13, and pinning a version that has no
|
|
68
|
+
# 3.14 wheels would defeat the purpose of an early warning.
|
|
69
|
+
- run: pip install -e ".[dev]"
|
|
70
|
+
- run: pytest -m "not live and not performance"
|
|
71
|
+
|
|
72
|
+
db-matrix:
|
|
73
|
+
name: tests (PostgreSQL)
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
services:
|
|
76
|
+
postgres:
|
|
77
|
+
image: postgres:16
|
|
78
|
+
# Credentials and database name match `weightsdb.testing`'s documented default URL, so the
|
|
79
|
+
# service this job starts is the one the package looks for.
|
|
80
|
+
env:
|
|
81
|
+
POSTGRES_USER: weightsdb
|
|
82
|
+
POSTGRES_PASSWORD: weightsdb
|
|
83
|
+
POSTGRES_DB: weightsdb_test
|
|
84
|
+
ports: ["5432:5432"]
|
|
85
|
+
options: >-
|
|
86
|
+
--health-cmd "pg_isready -U weightsdb -d weightsdb_test" --health-interval 5s --health-timeout 5s --health-retries 10
|
|
87
|
+
steps:
|
|
88
|
+
- uses: actions/checkout@v4
|
|
89
|
+
- uses: actions/setup-python@v5
|
|
90
|
+
with: { python-version: "3.12" }
|
|
91
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
92
|
+
- run: pip install . --no-deps
|
|
93
|
+
- run: pytest -m "not live and not performance" tests/integration
|
|
94
|
+
env:
|
|
95
|
+
WEIGHTSDB_REQUIRE_POSTGRES: "1"
|
|
96
|
+
# `temporary_postgres` reads WEIGHTSDB_POSTGRES_URL, not DATABASE_URL. Set explicitly
|
|
97
|
+
# rather than relying on the default, so the job states which server it is testing.
|
|
98
|
+
WEIGHTSDB_POSTGRES_URL: postgresql+psycopg://weightsdb:weightsdb@localhost:5432/weightsdb_test
|
|
99
|
+
|
|
100
|
+
coverage:
|
|
101
|
+
needs: [tests]
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v4
|
|
105
|
+
- uses: actions/setup-python@v5
|
|
106
|
+
with: { python-version: "3.12" }
|
|
107
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
108
|
+
- run: pip install . --no-deps
|
|
109
|
+
- run: pytest -m "not live and not performance" --cov --cov-report=term-missing --cov-fail-under=95
|
|
110
|
+
|
|
111
|
+
contracts:
|
|
112
|
+
runs-on: ubuntu-latest
|
|
113
|
+
steps:
|
|
114
|
+
- uses: actions/checkout@v4
|
|
115
|
+
- uses: actions/setup-python@v5
|
|
116
|
+
with: { python-version: "3.12" }
|
|
117
|
+
- run: pip install --require-hashes -r requirements/ci.lock
|
|
118
|
+
- run: pip install . --no-deps
|
|
119
|
+
- run: pytest -m contract
|
|
120
|
+
|
|
121
|
+
security:
|
|
122
|
+
runs-on: ubuntu-latest
|
|
123
|
+
steps:
|
|
124
|
+
- uses: actions/checkout@v4
|
|
125
|
+
# gitleaks scans *history*, and `actions/checkout` fetches a single commit by default.
|
|
126
|
+
# For a push it is handed `<first-pushed>^..<last-pushed>`, so the parent of the first
|
|
127
|
+
# pushed commit has to be in the object store; in a depth-1 clone it is not, and git
|
|
128
|
+
# answers "unknown revision", which the action reports as exit code 1. It fails the same
|
|
129
|
+
# way whether or not a secret exists, so a green run would not have meant anything either.
|
|
130
|
+
with: { fetch-depth: 0 }
|
|
131
|
+
- uses: actions/setup-python@v5
|
|
132
|
+
with: { python-version: "3.12" }
|
|
133
|
+
- run: pip install pip-audit
|
|
134
|
+
# Audit the locked sets, not the job's own environment: a bare `pip-audit` here would
|
|
135
|
+
# inspect an environment containing only pip-audit itself (Security Standards §11).
|
|
136
|
+
- run: pip-audit --require-hashes -r requirements/ci.lock
|
|
137
|
+
- run: pip-audit --require-hashes -r requirements/release.lock
|
|
138
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
139
|
+
env:
|
|
140
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
141
|
+
|
|
142
|
+
build:
|
|
143
|
+
runs-on: ubuntu-latest
|
|
144
|
+
steps:
|
|
145
|
+
- uses: actions/checkout@v4
|
|
146
|
+
- uses: actions/setup-python@v5
|
|
147
|
+
with: { python-version: "3.12" }
|
|
148
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
149
|
+
- run: python -m build --no-isolation
|
|
150
|
+
- run: twine check dist/*
|
|
151
|
+
- uses: actions/upload-artifact@v4
|
|
152
|
+
with: { name: dist, path: dist/ }
|
|
153
|
+
|
|
154
|
+
install-check:
|
|
155
|
+
needs: [build]
|
|
156
|
+
runs-on: ubuntu-latest
|
|
157
|
+
steps:
|
|
158
|
+
- uses: actions/checkout@v4
|
|
159
|
+
- uses: actions/setup-python@v5
|
|
160
|
+
with: { python-version: "3.12" }
|
|
161
|
+
- uses: actions/download-artifact@v4
|
|
162
|
+
with: { name: dist, path: dist/ }
|
|
163
|
+
- run: pip install dist/*.whl
|
|
164
|
+
- run: python -c "import weightsdb"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*.*.*"]
|
|
6
|
+
workflow_dispatch: # manual TestPyPI dry run; see publish-testpypi below
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # required for PyPI Trusted Publishing
|
|
10
|
+
contents: write # required to create the GitHub release
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release:
|
|
14
|
+
# Tag pushes only. Without this, clicking "Run workflow" for the TestPyPI dry run below would
|
|
15
|
+
# also fire this job and publish to real PyPI — the exact opposite of a dry run.
|
|
16
|
+
if: github.event_name == 'push'
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment: pypi # must match the Environment name set on the PyPI trusted publisher
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with: { python-version: "3.12" }
|
|
23
|
+
# Byte-for-byte the dry run's build chain below. A real release that resolved `build` and
|
|
24
|
+
# `hatchling` fresh from PyPI would not be the artifact the dry run proved.
|
|
25
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
26
|
+
- run: python -m build --no-isolation
|
|
27
|
+
- run: twine check dist/*
|
|
28
|
+
- run: pip install "dist/$(ls dist | grep .whl)[dev]"
|
|
29
|
+
- run: pytest -m "not live and not performance"
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
|
+
- name: Create GitHub release
|
|
33
|
+
uses: softprops/action-gh-release@v2
|
|
34
|
+
with:
|
|
35
|
+
generate_release_notes: true
|
|
36
|
+
files: dist/*
|
|
37
|
+
|
|
38
|
+
publish-testpypi:
|
|
39
|
+
# Manual only, via Actions -> Release -> Run workflow. Packaging and Release Standards §6
|
|
40
|
+
# requires a successful TestPyPI publish ahead of a package's first real release; 0.2.0 is
|
|
41
|
+
# this package's first published version, so run this once before tagging v0.2.0. Later
|
|
42
|
+
# releases may skip it, or use it again as a dry run.
|
|
43
|
+
if: github.event_name == 'workflow_dispatch'
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with: { python-version: "3.12" }
|
|
49
|
+
- run: pip install --require-hashes -r requirements/release.lock
|
|
50
|
+
- run: python -m build --no-isolation
|
|
51
|
+
- run: twine check dist/*
|
|
52
|
+
- run: pip install "dist/$(ls dist | grep .whl)[dev]"
|
|
53
|
+
- run: pytest -m "not live and not performance"
|
|
54
|
+
- name: Publish to TestPyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
with:
|
|
57
|
+
repository-url: https://test.pypi.org/legacy/
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# ---- Python ----
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# ---- Packaging / build backends ----
|
|
25
|
+
pip-wheel-metadata/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
|
|
28
|
+
# ---- Testing / coverage ----
|
|
29
|
+
.pytest_cache/
|
|
30
|
+
.cache
|
|
31
|
+
.coverage
|
|
32
|
+
.coverage.*
|
|
33
|
+
coverage.xml
|
|
34
|
+
*.cover
|
|
35
|
+
*.py,cover
|
|
36
|
+
htmlcov/
|
|
37
|
+
nosetests.xml
|
|
38
|
+
.hypothesis/
|
|
39
|
+
|
|
40
|
+
# ---- Type checking / linting ----
|
|
41
|
+
.mypy_cache/
|
|
42
|
+
.dmypy.json
|
|
43
|
+
dmypy.json
|
|
44
|
+
.ruff_cache/
|
|
45
|
+
.pytype/
|
|
46
|
+
|
|
47
|
+
# ---- Virtual environments ----
|
|
48
|
+
.venv/
|
|
49
|
+
venv/
|
|
50
|
+
ENV/
|
|
51
|
+
env/
|
|
52
|
+
env.bak/
|
|
53
|
+
venv.bak/
|
|
54
|
+
.python-version
|
|
55
|
+
|
|
56
|
+
# ---- Distribution / dependency locking ----
|
|
57
|
+
# requirements/*.lock is deliberately NOT ignored. The lock files are committed
|
|
58
|
+
# inputs: CI and release jobs install from them with --require-hashes (Packaging
|
|
59
|
+
# and Release Standards §4, Security Standards §11), so an ignored lock file
|
|
60
|
+
# means a checkout that cannot build.
|
|
61
|
+
|
|
62
|
+
# ---- Editors / OS ----
|
|
63
|
+
.vscode/
|
|
64
|
+
.idea/
|
|
65
|
+
*.swp
|
|
66
|
+
*.swo
|
|
67
|
+
.DS_Store
|
|
68
|
+
Thumbs.db
|
|
69
|
+
|
|
70
|
+
# ---- Suite runtime state (this component's local data) ----
|
|
71
|
+
# The application writes to XDG paths at runtime (~/.config, ~/.local/share,
|
|
72
|
+
# ~/.local/state per Master Architecture §1.2), never inside the repository.
|
|
73
|
+
# These entries only guard against a developer pointing XDG_* at the repo
|
|
74
|
+
# during local testing.
|
|
75
|
+
.local/
|
|
76
|
+
.config/
|
|
77
|
+
*.sqlite3
|
|
78
|
+
*.sqlite3-journal
|
|
79
|
+
*.sqlite3-wal
|
|
80
|
+
*.sqlite3-shm
|
|
81
|
+
/data/
|
|
82
|
+
/logs/
|
|
83
|
+
/backups/
|
|
84
|
+
/artifacts/
|
|
85
|
+
/exports/
|
|
86
|
+
|
|
87
|
+
# ---- Secrets ----
|
|
88
|
+
# Per Security Standards §8: secrets are never committed. Config files may
|
|
89
|
+
# only name where a secret comes from (*_env / *_file), never the value.
|
|
90
|
+
.env
|
|
91
|
+
.env.*
|
|
92
|
+
*.key
|
|
93
|
+
*.pem
|
|
94
|
+
secrets.toml
|
|
95
|
+
|
|
96
|
+
# ---- Node-free JS assets (MirrorWall consumers may still use a local tool) ----
|
|
97
|
+
node_modules/
|
|
98
|
+
|
|
99
|
+
# ---- Build artifacts from docs generation ----
|
|
100
|
+
docs/api/openapi-v1.json.tmp
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[importlinter]
|
|
2
|
+
root_package = weightsdb
|
|
3
|
+
include_external_packages = True
|
|
4
|
+
|
|
5
|
+
[importlinter:contract:no-application-imports]
|
|
6
|
+
name = WeightsDB must not import applications
|
|
7
|
+
type = forbidden
|
|
8
|
+
source_modules = weightsdb
|
|
9
|
+
forbidden_modules =
|
|
10
|
+
freeweight
|
|
11
|
+
loadcoach
|
|
12
|
+
ideapress
|
|
13
|
+
|
|
14
|
+
[importlinter:contract:no-sibling-packages]
|
|
15
|
+
name = WeightsDB must not import sibling capability packages
|
|
16
|
+
type = forbidden
|
|
17
|
+
source_modules = weightsdb
|
|
18
|
+
forbidden_modules =
|
|
19
|
+
modelrack
|
|
20
|
+
sweatmeter
|
|
21
|
+
mirrorwall
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.6.9
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v4.6.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: end-of-file-fixer
|
|
13
|
+
- id: check-toml
|
|
14
|
+
- id: check-json
|
|
15
|
+
- id: check-added-large-files
|
|
16
|
+
- id: check-merge-conflict
|
|
17
|
+
- id: mixed-line-ending
|
|
18
|
+
args: [--fix=lf]
|
|
19
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
20
|
+
rev: v8.18.4
|
|
21
|
+
hooks:
|
|
22
|
+
- id: gitleaks
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `weightsdb` are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
|
|
5
|
+
[Semantic Versioning](https://semver.org/), pre-1.0 per
|
|
6
|
+
packaging and release standards §3.
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Coverage fell below the 95 % floor on Python 3.12 (94.89 %).** The uncovered lines were real
|
|
12
|
+
gaps, not measurement noise: `upsert`'s documented refusal of a third dialect, the `StorageBusy`
|
|
13
|
+
translation on a *read-only* `BEGIN`, `temporary_postgres`'s `WEIGHTSDB_REQUIRE_POSTGRES=1`
|
|
14
|
+
failure path, longest-prefix mount selection against an unsorted mount table, and two backups
|
|
15
|
+
sharing an mtime. Each now has a test; the floor is met on 3.12, 3.13 and 3.14 with margin.
|
|
16
|
+
|
|
17
|
+
## [0.2.0] — 2026-08-29
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Repository scaffold generated from the suite's development plan (no functional code yet).
|
|
21
|
+
- Phase 1: `create_engine_for`, `session_factory`/`session_scope`/`transaction`, `UtcDateTime`,
|
|
22
|
+
`PortableJSON`, `ulid_primary_key`, `measurement_columns`, `upsert`, the `DatabaseError` hierarchy,
|
|
23
|
+
`redact_url`, and `weightsdb.testing.temporary_sqlite`/`temporary_postgres` — extracted from
|
|
24
|
+
FreeWeight's `infrastructure.db` (ADR-0011).
|
|
25
|
+
- Phase 2: `MigrationRunner` (upgrade/downgrade/stamp/check_parity with automatic pre-migration
|
|
26
|
+
backup and SQLite restore-on-failure), `backup`/`restore`/`integrity_check` and friends, and
|
|
27
|
+
`weightsdb.testing.migration_harness`.
|
|
28
|
+
- Phase 3: `database_health()` (dialect/version, revision vs head, journal mode, size, free space,
|
|
29
|
+
last backup age, integrity, network-filesystem warning, one `status`/`degraded_reasons` verdict),
|
|
30
|
+
`is_network_filesystem()`, the `py.typed` marker, `docs/quickstart.md` and
|
|
31
|
+
`docs/adoption-checklist.md` for FreeWeight Phase 12.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- SQLite lock contention beyond `busy_timeout` now raises the typed `StorageBusy` instead of a raw
|
|
35
|
+
`sqlalchemy.exc.OperationalError` — a gap in the code this package was extracted from, invisible
|
|
36
|
+
with one consumer and no test for it.
|
|
37
|
+
- **`psycopg[binary]` is now part of the `dev` extra.** Seventeen tests assert dialect-specific
|
|
38
|
+
behaviour by building a `postgresql://` engine — no server involved — which SQLAlchemy cannot do
|
|
39
|
+
without an importable DBAPI. Every environment that installed `[dev]` alone failed them with
|
|
40
|
+
`ModuleNotFoundError` and landed coverage at 88 % against a 95 % floor; the local `.venv` hid it
|
|
41
|
+
by having been created with `[dev,postgres]`. The optional `postgres` extra is unchanged, and is
|
|
42
|
+
still what a consumer installs to talk to a real server.
|
|
43
|
+
- **The `db-matrix` CI job read the wrong environment variable.** It set `DATABASE_URL`, which
|
|
44
|
+
nothing in this package reads; `weightsdb.testing.temporary_postgres` reads
|
|
45
|
+
`WEIGHTSDB_POSTGRES_URL` and otherwise falls back to its own default. With
|
|
46
|
+
`WEIGHTSDB_REQUIRE_POSTGRES=1` the fallback is a hard failure rather than a skip, so the job had
|
|
47
|
+
never exercised PostgreSQL at all. The service container's user, password and database now match
|
|
48
|
+
the documented default URL, and the job sets `WEIGHTSDB_POSTGRES_URL` explicitly rather than
|
|
49
|
+
relying on that default.
|
|
50
|
+
- **The PostgreSQL round-trip no longer fails on client/server version skew.** `pg_restore` exits 1
|
|
51
|
+
for warnings unrelated to the data — a client newer than the server emits
|
|
52
|
+
`SET transaction_timeout = 0`, which an older server rejects and reports as "errors ignored on
|
|
53
|
+
restore" — so the test asserted an exit code where its actual claim is that the rows come back.
|
|
54
|
+
It now checks the restored rows and attaches `pg_restore`'s stderr when they are wrong. A missing
|
|
55
|
+
`pg_dump` skips the test the way a missing `pg_restore` already did, instead of failing inside
|
|
56
|
+
`backup()`.
|
|
57
|
+
- **`test_restore_reports_a_backup_it_cannot_open` skips when running as root.** `chmod 000` does
|
|
58
|
+
not make a file unreadable to root, so the open succeeds and the corruption branch answers
|
|
59
|
+
instead — a failure that says nothing about the code. CI runs as an ordinary user; a container
|
|
60
|
+
run as root no longer reports it as a defect.
|
|
61
|
+
- The `dev` extra moves to `pytest>=9.0.3,<10`, matching BaseAiCore, SetSpec, ModelRack and
|
|
62
|
+
SweatMeter: PYSEC-2026-1845 affects pytest through 9.0.2 and failed the security job.
|
|
63
|
+
- **The backup tests' table seed was not dialect-portable.** `INSERT INTO t (name) …` relies on
|
|
64
|
+
SQLite treating `INTEGER PRIMARY KEY` as a rowid alias; PostgreSQL rejects the row with a
|
|
65
|
+
NOT NULL violation. The id is now supplied explicitly, so the round-trip test can actually run
|
|
66
|
+
on the dialect it names.
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
- CI installs from committed, hash-verified lockfiles (`requirements/ci.lock`,
|
|
70
|
+
`requirements/release.lock`) rather than an editable checkout, per Packaging Standards §4;
|
|
71
|
+
`pip-audit` audits those locks instead of an empty environment; `release.yml` gains the
|
|
72
|
+
`pypi` deployment environment, the manual TestPyPI dry run required before a first release, and
|
|
73
|
+
a build chain pinned byte-for-byte to the one the dry run proves. `[tool.coverage.run] source`
|
|
74
|
+
now names the importable package rather than `src/weightsdb`, because a non-editable install
|
|
75
|
+
reports 0 % against a path-based source.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Contributing to WeightsDB
|
|
2
|
+
|
|
3
|
+
This repository is one component of the Local AI Suite. Before changing anything, read
|
|
4
|
+
`docs/packages/weightsdb/spec.md` and the current
|
|
5
|
+
phase in `development-plan.md` — both are in this repository's `docs/` folder, copied from the suite's
|
|
6
|
+
central documentation set so this repository can be worked on independently.
|
|
7
|
+
|
|
8
|
+
## Development setup
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m venv .venv
|
|
12
|
+
source .venv/bin/activate
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
pre-commit install
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Required reading, in order
|
|
18
|
+
|
|
19
|
+
1. This component's spec — purpose, scope, non-goals, contracts.
|
|
20
|
+
2. `development-plan.md` in the same folder — the phase you are implementing, its acceptance criteria and its tests.
|
|
21
|
+
|
|
22
|
+
## Rules that apply to every change here
|
|
23
|
+
|
|
24
|
+
* Follow the architecture's dependency direction.
|
|
25
|
+
This repository's `.importlinter` enforces it in CI; do not weaken that file to make an import work.
|
|
26
|
+
* No business logic in a route handler or CLI command body — both call one service method and render
|
|
27
|
+
.
|
|
28
|
+
* An unavailable measurement is `Unsupported`, never zero, never `None` used as a substitute
|
|
29
|
+
.
|
|
30
|
+
* Prompts are versioned JSON records, not Python string literals.
|
|
31
|
+
* Every phase's acceptance criteria in `development-plan.md` must be demonstrable, not merely
|
|
32
|
+
test-covered — the plan states what to run and what a person should see.
|
|
33
|
+
|
|
34
|
+
## Before opening a pull request
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
ruff format --check .
|
|
38
|
+
ruff check .
|
|
39
|
+
mypy src tests
|
|
40
|
+
lint-imports
|
|
41
|
+
pytest -m "not live and not performance"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
All of the above run in CI (`.github/workflows/ci.yml`); a red CI run blocks merge.
|
|
45
|
+
|
|
46
|
+
## Commit style
|
|
47
|
+
|
|
48
|
+
Conventional Commits (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`, `perf:`, `build:`,
|
|
49
|
+
`ci:`), with `!` or a `BREAKING CHANGE:` footer for breaking changes. Update `CHANGELOG.md` under
|
|
50
|
+
`## [Unreleased]` for any user-visible change.
|