pico-sqlalchemy 0.2.1.dev0__tar.gz → 0.4.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.
- pico_sqlalchemy-0.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +32 -0
- pico_sqlalchemy-0.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +18 -0
- pico_sqlalchemy-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +10 -0
- pico_sqlalchemy-0.4.0/.github/dependabot.yml +11 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/.github/workflows/ci.yml +24 -3
- pico_sqlalchemy-0.4.0/.github/workflows/codeql.yml +28 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/.github/workflows/docs.yml +5 -5
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/.github/workflows/publish-to-pypi.yml +15 -3
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/.github/workflows/sync-keywords.yml +2 -2
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/AGENTS.md +4 -3
- {pico_sqlalchemy-0.2.1.dev0/docs → pico_sqlalchemy-0.4.0}/CHANGELOG.md +18 -0
- pico_sqlalchemy-0.4.0/CLAUDE.md +19 -0
- pico_sqlalchemy-0.4.0/CODE_OF_CONDUCT.md +31 -0
- pico_sqlalchemy-0.4.0/CONTRIBUTING.md +34 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/PKG-INFO +56 -26
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/README.md +50 -24
- pico_sqlalchemy-0.4.0/SECURITY.md +23 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0/docs}/CHANGELOG.md +18 -0
- pico_sqlalchemy-0.4.0/docs/architecture.md +488 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/development/project-tooling.md +3 -3
- pico_sqlalchemy-0.4.0/docs/faq.md +406 -0
- pico_sqlalchemy-0.4.0/docs/hooks.py +8 -0
- pico_sqlalchemy-0.4.0/docs/how-to/alembic.md +153 -0
- pico_sqlalchemy-0.4.0/docs/how-to/multiple-databases.md +217 -0
- pico_sqlalchemy-0.4.0/docs/how-to/testing.md +284 -0
- pico_sqlalchemy-0.4.0/docs/migration.md +198 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/overview.md +1 -1
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/quickstart.md +1 -1
- pico_sqlalchemy-0.4.0/docs/reference/configuration.md +139 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/reference/declarative-base.md +6 -1
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/reference/repository.md +2 -183
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/reference/transactions.md +6 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/requirements.txt +1 -0
- pico_sqlalchemy-0.4.0/docs/skills.md +59 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/README.md +25 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/app/__init__.py +0 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/app/main.py +47 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/app/models.py +12 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/app/repositories.py +43 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/app/services.py +26 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/config.yml +3 -0
- pico_sqlalchemy-0.4.0/examples/crud-async/requirements.txt +3 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/mkdocs.yml +14 -2
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/pyproject.toml +28 -2
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/__init__.py +51 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/_version.py +1 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/base.py +38 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/config.py +91 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/decorators.py +275 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/factory.py +95 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/interceptor.py +141 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/paging.py +115 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/py.typed +0 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/repository_interceptor.py +358 -0
- pico_sqlalchemy-0.4.0/src/pico_sqlalchemy/session.py +403 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/PKG-INFO +56 -26
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/SOURCES.txt +26 -2
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/requires.txt +1 -1
- pico_sqlalchemy-0.4.0/tests/conftest.py +57 -0
- pico_sqlalchemy-0.4.0/tests/test_coverage_boost_v2.py +276 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_interceptor.py +9 -25
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_ioc_integration.py +10 -26
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_pagination_sort.py +18 -40
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_repository_interceptor_coverage.py +30 -41
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_repository_query.py +12 -35
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_session_propagation.py +5 -16
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tests/test_transaction_manager.py +4 -6
- pico_sqlalchemy-0.4.0/tests/test_transaction_scope.py +133 -0
- pico_sqlalchemy-0.2.1.dev0/CLAUDE.md +0 -19
- pico_sqlalchemy-0.2.1.dev0/docs/architecture.md +0 -232
- pico_sqlalchemy-0.2.1.dev0/docs/faq.md +0 -136
- pico_sqlalchemy-0.2.1.dev0/docs/reference/configuration.md +0 -106
- pico_sqlalchemy-0.2.1.dev0/docs/skills.md +0 -117
- pico_sqlalchemy-0.2.1.dev0/manage.sh +0 -65
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/__init__.py +0 -27
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/_version.py +0 -1
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/base.py +0 -8
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/config.py +0 -22
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/decorators.py +0 -111
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/factory.py +0 -41
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/interceptor.py +0 -61
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/paging.py +0 -50
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/repository_interceptor.py +0 -184
- pico_sqlalchemy-0.2.1.dev0/src/pico_sqlalchemy/session.py +0 -212
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/.coveragerc +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/LICENSE +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/MANIFEST.in +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/index.md +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/javascripts/extra.js +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/docs/stylesheets/extra.css +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/setup.cfg +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/dependency_links.txt +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/entry_points.txt +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/src/pico_sqlalchemy.egg-info/top_level.txt +0 -0
- {pico_sqlalchemy-0.2.1.dev0 → pico_sqlalchemy-0.4.0}/tox.ini +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug in pico-sqlalchemy
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: Description
|
|
9
|
+
description: What happened?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: reproduction
|
|
14
|
+
attributes:
|
|
15
|
+
label: Steps to Reproduce
|
|
16
|
+
description: Minimal code or steps to reproduce the issue.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: input
|
|
20
|
+
id: version
|
|
21
|
+
attributes:
|
|
22
|
+
label: pico-sqlalchemy version
|
|
23
|
+
placeholder: "0.2.0"
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: input
|
|
27
|
+
id: python-version
|
|
28
|
+
attributes:
|
|
29
|
+
label: Python version
|
|
30
|
+
placeholder: "3.11"
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest an enhancement for pico-sqlalchemy
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What problem does this solve?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: solution
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed Solution
|
|
16
|
+
description: How should it work?
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- Brief description of the changes -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] Tests pass (`pytest tests/ -v`)
|
|
8
|
+
- [ ] Code formatted (`ruff format src/ tests/`)
|
|
9
|
+
- [ ] Lint clean (`ruff check src/ tests/`)
|
|
10
|
+
- [ ] CHANGELOG.md updated (if user-facing change)
|
|
@@ -3,8 +3,23 @@ name: CI & Coverage
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
6
8
|
|
|
7
9
|
jobs:
|
|
10
|
+
lint:
|
|
11
|
+
name: Lint & Format
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v6
|
|
15
|
+
- uses: actions/setup-python@v6
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.11'
|
|
18
|
+
cache: pip
|
|
19
|
+
- run: pip install ruff
|
|
20
|
+
- run: ruff check src/ tests/
|
|
21
|
+
- run: ruff format --check src/ tests/
|
|
22
|
+
|
|
8
23
|
tests:
|
|
9
24
|
name: Tests (Python ${{ matrix.python-version }})
|
|
10
25
|
runs-on: ubuntu-latest
|
|
@@ -15,12 +30,12 @@ jobs:
|
|
|
15
30
|
|
|
16
31
|
steps:
|
|
17
32
|
- name: Checkout
|
|
18
|
-
uses: actions/checkout@
|
|
33
|
+
uses: actions/checkout@v6
|
|
19
34
|
with:
|
|
20
35
|
fetch-depth: 0
|
|
21
36
|
|
|
22
37
|
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
-
uses: actions/setup-python@
|
|
38
|
+
uses: actions/setup-python@v6
|
|
24
39
|
with:
|
|
25
40
|
python-version: ${{ matrix.python-version }}
|
|
26
41
|
cache: pip
|
|
@@ -30,6 +45,12 @@ jobs:
|
|
|
30
45
|
python -m pip install --upgrade pip
|
|
31
46
|
pip install tox
|
|
32
47
|
|
|
48
|
+
- name: Cache tox environments
|
|
49
|
+
uses: actions/cache@v5
|
|
50
|
+
with:
|
|
51
|
+
path: .tox
|
|
52
|
+
key: tox-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml', 'tox.ini') }}
|
|
53
|
+
|
|
33
54
|
- name: Install build dependencies
|
|
34
55
|
run: |
|
|
35
56
|
sudo apt-get update
|
|
@@ -58,7 +79,7 @@ jobs:
|
|
|
58
79
|
needs: tests
|
|
59
80
|
steps:
|
|
60
81
|
- name: Checkout (for repo context)
|
|
61
|
-
uses: actions/checkout@
|
|
82
|
+
uses: actions/checkout@v6
|
|
62
83
|
|
|
63
84
|
- name: Download all coverage artifacts
|
|
64
85
|
uses: actions/download-artifact@v4
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 6 * * 1'
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
analyze:
|
|
13
|
+
name: Analyze (Python)
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
permissions:
|
|
16
|
+
security-events: write
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v6
|
|
21
|
+
|
|
22
|
+
- name: Initialize CodeQL
|
|
23
|
+
uses: github/codeql-action/init@v4
|
|
24
|
+
with:
|
|
25
|
+
languages: python
|
|
26
|
+
|
|
27
|
+
- name: Perform CodeQL Analysis
|
|
28
|
+
uses: github/codeql-action/analyze@v4
|
|
@@ -29,11 +29,11 @@ jobs:
|
|
|
29
29
|
if: github.event_name == 'pull_request'
|
|
30
30
|
runs-on: ubuntu-latest
|
|
31
31
|
steps:
|
|
32
|
-
- uses: actions/checkout@
|
|
32
|
+
- uses: actions/checkout@v6
|
|
33
33
|
with:
|
|
34
34
|
fetch-depth: 0
|
|
35
35
|
|
|
36
|
-
- uses: actions/setup-python@
|
|
36
|
+
- uses: actions/setup-python@v6
|
|
37
37
|
with:
|
|
38
38
|
python-version: '3.11'
|
|
39
39
|
cache: 'pip'
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
pip install linkchecker
|
|
52
52
|
python -m http.server --directory site 8000 &
|
|
53
53
|
sleep 3
|
|
54
|
-
linkchecker http://127.0.0.1:8000 --check-extern
|
|
54
|
+
linkchecker http://127.0.0.1:8000 --check-extern
|
|
55
55
|
|
|
56
56
|
deploy:
|
|
57
57
|
if: github.event_name != 'pull_request'
|
|
@@ -61,11 +61,11 @@ jobs:
|
|
|
61
61
|
url: ${{ steps.deployment.outputs.page_url }}
|
|
62
62
|
|
|
63
63
|
steps:
|
|
64
|
-
- uses: actions/checkout@
|
|
64
|
+
- uses: actions/checkout@v6
|
|
65
65
|
with:
|
|
66
66
|
fetch-depth: 0
|
|
67
67
|
|
|
68
|
-
- uses: actions/setup-python@
|
|
68
|
+
- uses: actions/setup-python@v6
|
|
69
69
|
with:
|
|
70
70
|
python-version: '3.11'
|
|
71
71
|
cache: 'pip'
|
|
@@ -12,12 +12,12 @@ jobs:
|
|
|
12
12
|
contents: read
|
|
13
13
|
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
15
|
+
- uses: actions/checkout@v6
|
|
16
16
|
with:
|
|
17
17
|
fetch-depth: 0
|
|
18
18
|
|
|
19
19
|
- name: Set up Python
|
|
20
|
-
uses: actions/setup-python@
|
|
20
|
+
uses: actions/setup-python@v6
|
|
21
21
|
with:
|
|
22
22
|
python-version: '3.x'
|
|
23
23
|
|
|
@@ -27,10 +27,22 @@ jobs:
|
|
|
27
27
|
- name: Build package
|
|
28
28
|
run: python -m build
|
|
29
29
|
|
|
30
|
+
- name: Reject non-clean versions (.dev / .post)
|
|
31
|
+
run: |
|
|
32
|
+
python - <<'PY'
|
|
33
|
+
import glob, sys
|
|
34
|
+
whl = sorted(glob.glob('dist/*.whl'))[0]
|
|
35
|
+
ver = whl.split('-')[1]
|
|
36
|
+
print(f"Built version: {ver}")
|
|
37
|
+
if 'dev' in ver or 'post' in ver:
|
|
38
|
+
sys.exit(f"::error::Refusing to publish non-clean version '{ver}'. "
|
|
39
|
+
"Create the GitHub Release from an exact version tag (e.g. v0.4.0).")
|
|
40
|
+
PY
|
|
41
|
+
|
|
30
42
|
- name: Publish to PyPI
|
|
31
43
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
32
44
|
with:
|
|
33
45
|
skip-existing: true
|
|
34
46
|
verify-metadata: true
|
|
35
|
-
attestations:
|
|
47
|
+
attestations: true
|
|
36
48
|
|
|
@@ -35,9 +35,10 @@ src/pico_sqlalchemy/
|
|
|
35
35
|
- **Propagation modes**: REQUIRED, REQUIRES_NEW, MANDATORY, NEVER, NOT_SUPPORTED, SUPPORTS
|
|
36
36
|
- **Priority chain**: `@transactional` > `@query` (read-only) > `@repository` (read-write)
|
|
37
37
|
- **`SessionManager`**: Created by `SqlAlchemyFactory` (not `@component`). Manages engine, sessions, transactions
|
|
38
|
-
- **`_tx_context` ContextVar**: Holds `TransactionContext`
|
|
38
|
+
- **`_tx_context` ContextVar**: Holds `TransactionContext` (the session) for propagation across nested calls — the *session* side of a transaction
|
|
39
|
+
- **`"transaction"` DI scope**: `TransactionalInterceptor` activates pico-ioc's `"transaction"` scope (with `cleanup=True`) whenever a *new* transaction starts (REQUIRES_NEW, or REQUIRED with no enclosing tx). So a `scope="transaction"` component is one instance per transaction (Unit-of-Work / identity-map), released with its `@cleanup` hooks when the tx ends. The session ContextVar and the DI scope are two facets of one boundary
|
|
39
40
|
- **`get_session(manager)`**: Returns current session from active transaction context
|
|
40
|
-
- **Non-transactional paths** (NEVER, NOT_SUPPORTED, SUPPORTS without tx): Still set `TransactionContext` so `get_session()` works
|
|
41
|
+
- **Non-transactional paths** (NEVER, NOT_SUPPORTED, SUPPORTS without tx): Still set `TransactionContext` so `get_session()` works, but open no DI `"transaction"` scope (resolving a `scope="transaction"` component there raises `ScopeError`)
|
|
41
42
|
|
|
42
43
|
## Code Style
|
|
43
44
|
|
|
@@ -59,4 +60,4 @@ src/pico_sqlalchemy/
|
|
|
59
60
|
|
|
60
61
|
- Do not modify `_version.py`
|
|
61
62
|
- Do not add `@component` to `SessionManager` (factory creates it)
|
|
62
|
-
- `_tx_context`
|
|
63
|
+
- `_tx_context` (session propagation) and the pico-ioc `"transaction"` DI scope are bound to one boundary by `TransactionalInterceptor` — keep them coordinated; do not reintroduce a second, independent transaction concept
|
|
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.h
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-06-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Transaction-scoped DI**: `TransactionalInterceptor` now binds pico-ioc's `"transaction"` DI scope to the database transaction boundary. When a *new* transaction starts (`REQUIRES_NEW`, or `REQUIRED` with no enclosing transaction) it activates a fresh `"transaction"` scope (via `container.scope(..., cleanup=True)`) for the duration of the call, so components registered with `scope="transaction"` live exactly one transaction and run their `@cleanup` hooks when it ends. Joins reuse the enclosing scope. Requires **pico-ioc >= 2.2.6**.
|
|
14
|
+
- `tests/test_transaction_scope.py`: covers one-instance-per-transaction, sharing within a transaction, `REQUIRES_NEW` scope push/restore, fail-fast resolution outside a transaction, and `@cleanup` on transaction end.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Bumped `pico-ioc` dependency to `>= 2.2.6`.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## [0.3.0] - 2026-02-20
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- **Breaking:** Renamed `DatabaseConfigurer.configure(engine)` to `configure_database(engine)` to avoid protocol collision with `FastApiConfigurer.configure(app)` in structural typing.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
10
28
|
## [0.2.0] - 2026-02-06
|
|
11
29
|
|
|
12
30
|
### Added
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Read and follow ./AGENTS.md for project conventions.
|
|
2
|
+
|
|
3
|
+
## Pico Ecosystem Context
|
|
4
|
+
|
|
5
|
+
pico-sqlalchemy provides SQLAlchemy integration for pico-ioc. It uses:
|
|
6
|
+
- `@factory` + `@provides` for SessionManager creation
|
|
7
|
+
- `@configured` for DatabaseSettings
|
|
8
|
+
- `MethodInterceptor` for both `TransactionalInterceptor` and `RepositoryQueryInterceptor`
|
|
9
|
+
- Auto-discovered via `pico_boot.modules` entry point
|
|
10
|
+
|
|
11
|
+
## Key Reminders
|
|
12
|
+
|
|
13
|
+
- pico-ioc dependency: `>= 2.2.6` (needs `container.scope(..., cleanup=True)`)
|
|
14
|
+
- **NEVER change `version_scheme`** in pyproject.toml. It MUST remain `"post-release"`. Changing it to `"guess-next-dev"` causes `.dev0` versions to leak to PyPI. This was already fixed once — do not revert it.
|
|
15
|
+
- requires-python >= 3.11
|
|
16
|
+
- Commit messages: one line only
|
|
17
|
+
- `@transactional` works both with and without parentheses (like `@repository`)
|
|
18
|
+
- `SessionManager` has NO `@component` decorator - it's created by the factory
|
|
19
|
+
- `_tx_context` ContextVar is the *session* side of a transaction (propagation across nested calls). `TransactionalInterceptor` binds pico-ioc's `"transaction"` DI scope to the *same* boundary: when a new transaction starts (REQUIRES_NEW, or REQUIRED with no enclosing tx) it activates a fresh `"transaction"` scope (with `cleanup=True`) for the call, so `scope="transaction"` components live exactly one transaction. They are two facets of one boundary, not separate mechanisms.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone.
|
|
7
|
+
|
|
8
|
+
## Our Standards
|
|
9
|
+
|
|
10
|
+
Examples of behavior that contributes to a positive environment:
|
|
11
|
+
|
|
12
|
+
* Using welcoming and inclusive language
|
|
13
|
+
* Being respectful of differing viewpoints and experiences
|
|
14
|
+
* Gracefully accepting constructive criticism
|
|
15
|
+
* Focusing on what is best for the community
|
|
16
|
+
|
|
17
|
+
Examples of unacceptable behavior:
|
|
18
|
+
|
|
19
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
20
|
+
* Public or private harassment
|
|
21
|
+
* Publishing others' private information without explicit permission
|
|
22
|
+
|
|
23
|
+
## Enforcement
|
|
24
|
+
|
|
25
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
26
|
+
reported to **dperezcabrera@gmail.com**. All complaints will be reviewed
|
|
27
|
+
and investigated promptly and fairly.
|
|
28
|
+
|
|
29
|
+
## Attribution
|
|
30
|
+
|
|
31
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Contributing to Pico-SQLAlchemy
|
|
2
|
+
|
|
3
|
+
## Development Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
python -m venv .venv
|
|
7
|
+
source .venv/bin/activate
|
|
8
|
+
pip install -e ".[test]"
|
|
9
|
+
pip install ruff
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Running Tests
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pytest tests/ -v # Run tests
|
|
16
|
+
tox # Full matrix (3.11-3.14)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Code Style
|
|
20
|
+
|
|
21
|
+
- Python 3.11+
|
|
22
|
+
- Format with `ruff format src/ tests/`
|
|
23
|
+
- Lint with `ruff check src/ tests/`
|
|
24
|
+
|
|
25
|
+
## Pull Requests
|
|
26
|
+
|
|
27
|
+
1. Fork the repository
|
|
28
|
+
2. Create a feature branch
|
|
29
|
+
3. Ensure tests pass and code is formatted
|
|
30
|
+
4. Submit a PR with a clear description
|
|
31
|
+
|
|
32
|
+
## Reporting Issues
|
|
33
|
+
|
|
34
|
+
Use [GitHub Issues](https://github.com/dperezcabrera/pico-sqlalchemy/issues) for bugs and feature requests.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pico-sqlalchemy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Pico-ioc integration for SQLAlchemy. Adds Spring-style transactional support, configuration, and helpers.
|
|
5
5
|
Author-email: David Perez Cabrera <dperezcabrera@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -28,6 +28,8 @@ License: MIT License
|
|
|
28
28
|
Project-URL: Homepage, https://github.com/dperezcabrera/pico-sqlalchemy
|
|
29
29
|
Project-URL: Repository, https://github.com/dperezcabrera/pico-sqlalchemy
|
|
30
30
|
Project-URL: Issue Tracker, https://github.com/dperezcabrera/pico-sqlalchemy/issues
|
|
31
|
+
Project-URL: Documentation, https://dperezcabrera.github.io/pico-sqlalchemy/
|
|
32
|
+
Project-URL: Changelog, https://github.com/dperezcabrera/pico-sqlalchemy/blob/main/CHANGELOG.md
|
|
31
33
|
Keywords: ioc,di,dependency injection,sqlalchemy,transaction,orm,inversion of control,asyncio
|
|
32
34
|
Classifier: Development Status :: 4 - Beta
|
|
33
35
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@@ -38,12 +40,14 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
38
40
|
Classifier: Programming Language :: Python :: 3.12
|
|
39
41
|
Classifier: Programming Language :: Python :: 3.13
|
|
40
42
|
Classifier: Programming Language :: Python :: 3.14
|
|
43
|
+
Classifier: Intended Audience :: Developers
|
|
41
44
|
Classifier: License :: OSI Approved :: MIT License
|
|
42
45
|
Classifier: Operating System :: OS Independent
|
|
46
|
+
Classifier: Typing :: Typed
|
|
43
47
|
Requires-Python: >=3.11
|
|
44
48
|
Description-Content-Type: text/markdown
|
|
45
49
|
License-File: LICENSE
|
|
46
|
-
Requires-Dist: pico-ioc>=2.2.
|
|
50
|
+
Requires-Dist: pico-ioc>=2.2.6
|
|
47
51
|
Requires-Dist: sqlalchemy>=2.0
|
|
48
52
|
Provides-Extra: async
|
|
49
53
|
Requires-Dist: asyncpg>=0.29.0; extra == "async"
|
|
@@ -53,7 +57,7 @@ Requires-Dist: pytest-asyncio>=0.23.5; extra == "test"
|
|
|
53
57
|
Requires-Dist: pytest-cov>=5; extra == "test"
|
|
54
58
|
Dynamic: license-file
|
|
55
59
|
|
|
56
|
-
#
|
|
60
|
+
# pico-sqlalchemy
|
|
57
61
|
|
|
58
62
|
[](https://pypi.org/project/pico-sqlalchemy/)
|
|
59
63
|
[](https://deepwiki.com/dperezcabrera/pico-sqlalchemy)
|
|
@@ -64,6 +68,7 @@ Dynamic: license-file
|
|
|
64
68
|
[](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-sqlalchemy)
|
|
65
69
|
[](https://sonarcloud.io/summary/new_code?id=dperezcabrera_pico-sqlalchemy)
|
|
66
70
|
[](https://dperezcabrera.github.io/pico-sqlalchemy/)
|
|
71
|
+
[](https://dperezcabrera.github.io/pico-learn/)
|
|
67
72
|
|
|
68
73
|
# Pico-SQLAlchemy
|
|
69
74
|
|
|
@@ -71,14 +76,14 @@ Dynamic: license-file
|
|
|
71
76
|
|
|
72
77
|
It brings constructor-based dependency injection, **implicit transaction management**, and powerful **declarative queries** using pure Python and SQLAlchemy’s Async ORM.
|
|
73
78
|
|
|
74
|
-
>
|
|
75
|
-
>
|
|
76
|
-
>
|
|
77
|
-
>
|
|
79
|
+
> **Requires Python 3.11+**
|
|
80
|
+
> **Async-Native:** Built entirely on `AsyncSession` and `create_async_engine`.
|
|
81
|
+
> **Zero-Boilerplate:** Repositories are transactional by default.
|
|
82
|
+
> **Declarative Queries:** Define SQL or expressions in decorators; the library executes them for you.
|
|
78
83
|
|
|
79
84
|
---
|
|
80
85
|
|
|
81
|
-
##
|
|
86
|
+
## Why pico-sqlalchemy?
|
|
82
87
|
|
|
83
88
|
Most Python apps suffer from manual session handling (`async with session...`), scattered transaction logic, and verbose repository patterns.
|
|
84
89
|
|
|
@@ -94,7 +99,7 @@ Most Python apps suffer from manual session handling (`async with session...`),
|
|
|
94
99
|
|
|
95
100
|
---
|
|
96
101
|
|
|
97
|
-
##
|
|
102
|
+
## Core Features
|
|
98
103
|
|
|
99
104
|
* **Implicit Transactions:** Methods inside `@repository` are automatically **Read-Write** transactional.
|
|
100
105
|
* **Declarative Queries:** Use `@query` to run SQL or Expressions automatically (defaults to **Read-Only**).
|
|
@@ -104,7 +109,7 @@ Most Python apps suffer from manual session handling (`async with session...`),
|
|
|
104
109
|
|
|
105
110
|
---
|
|
106
111
|
|
|
107
|
-
##
|
|
112
|
+
## Installation
|
|
108
113
|
|
|
109
114
|
```bash
|
|
110
115
|
pip install pico-sqlalchemy
|
|
@@ -119,7 +124,7 @@ pip install asyncpg # for PostgreSQL
|
|
|
119
124
|
|
|
120
125
|
-----
|
|
121
126
|
|
|
122
|
-
##
|
|
127
|
+
## Quick Example
|
|
123
128
|
|
|
124
129
|
### 1\. Define Model
|
|
125
130
|
|
|
@@ -214,7 +219,7 @@ if __name__ == "__main__":
|
|
|
214
219
|
|
|
215
220
|
-----
|
|
216
221
|
|
|
217
|
-
##
|
|
222
|
+
## Transaction Hierarchy & Rules
|
|
218
223
|
|
|
219
224
|
Pico-SQLAlchemy applies a "Best Effort" strategy to determine transaction configuration. The priority order (highest wins) is:
|
|
220
225
|
|
|
@@ -232,7 +237,7 @@ Pico-SQLAlchemy applies a "Best Effort" strategy to determine transaction config
|
|
|
232
237
|
async def update_user(self): ...
|
|
233
238
|
```
|
|
234
239
|
|
|
235
|
-
|
|
240
|
+
**Result:** Active Read-Write Transaction (Implicit from `@repository`).
|
|
236
241
|
|
|
237
242
|
2. **Query Method:**
|
|
238
243
|
|
|
@@ -241,7 +246,7 @@ Pico-SQLAlchemy applies a "Best Effort" strategy to determine transaction config
|
|
|
241
246
|
async def get_data(self): ...
|
|
242
247
|
```
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
**Result:** Active Read-Only Transaction (Implicit from `@query`).
|
|
245
250
|
|
|
246
251
|
3. **Manual Override:**
|
|
247
252
|
|
|
@@ -250,11 +255,29 @@ Pico-SQLAlchemy applies a "Best Effort" strategy to determine transaction config
|
|
|
250
255
|
async def complex_report(self): ...
|
|
251
256
|
```
|
|
252
257
|
|
|
253
|
-
|
|
258
|
+
**Result:** Active Read-Only Transaction (Explicit override).
|
|
259
|
+
|
|
260
|
+
### Transaction-scoped components *(v0.4.0+)*
|
|
261
|
+
|
|
262
|
+
Beyond managing the SQLAlchemy session, the interceptor binds pico-ioc's **`"transaction"` DI scope** to the same boundary. A component registered with `scope="transaction"` is instantiated **once per database transaction** and torn down (running its `@cleanup` hooks) when that transaction ends:
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
@component(scope="transaction")
|
|
266
|
+
class UnitOfWorkAudit:
|
|
267
|
+
def __init__(self):
|
|
268
|
+
self.events: list[str] = []
|
|
269
|
+
|
|
270
|
+
@cleanup
|
|
271
|
+
def flush(self):
|
|
272
|
+
# runs exactly when the enclosing transaction ends
|
|
273
|
+
...
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
A **new** transaction (`REQUIRES_NEW`, or `REQUIRED` with no enclosing transaction) opens a fresh scope; **joins reuse** the enclosing one — so the session boundary and the DI lifetime are two facets of a single transaction. Requires **pico-ioc ≥ 2.2.6**.
|
|
254
277
|
|
|
255
278
|
-----
|
|
256
279
|
|
|
257
|
-
##
|
|
280
|
+
## Declarative Queries in Depth
|
|
258
281
|
|
|
259
282
|
The `@query` decorator eliminates boilerplate for common fetches.
|
|
260
283
|
|
|
@@ -289,7 +312,7 @@ async def find_active(self, page: PageRequest) -> Page[User]: ...
|
|
|
289
312
|
|
|
290
313
|
-----
|
|
291
314
|
|
|
292
|
-
##
|
|
315
|
+
## Testing
|
|
293
316
|
|
|
294
317
|
Testing is simple because you can override the configuration or the components easily using Pico-IoC.
|
|
295
318
|
|
|
@@ -307,7 +330,7 @@ async def test_service():
|
|
|
307
330
|
|
|
308
331
|
-----
|
|
309
332
|
|
|
310
|
-
##
|
|
333
|
+
## Architecture Overview
|
|
311
334
|
|
|
312
335
|
```
|
|
313
336
|
┌─────────────────────────────┐
|
|
@@ -333,19 +356,26 @@ async def test_service():
|
|
|
333
356
|
|
|
334
357
|
-----
|
|
335
358
|
|
|
336
|
-
##
|
|
359
|
+
## AI Coding Skills
|
|
360
|
+
|
|
361
|
+
Install [Claude Code](https://code.claude.com) or [OpenAI Codex](https://openai.com/index/introducing-codex/) skills for AI-assisted development with pico-sqlalchemy:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
curl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash -s -- sqlalchemy
|
|
365
|
+
```
|
|
337
366
|
|
|
338
|
-
|
|
367
|
+
| Command | Description |
|
|
368
|
+
|---------|-------------|
|
|
369
|
+
| `/add-repository` | Add SQLAlchemy entities and repositories with transactions |
|
|
370
|
+
| `/add-component` | Add components, factories, interceptors, settings |
|
|
371
|
+
| `/add-tests` | Generate tests for pico components |
|
|
339
372
|
|
|
340
|
-
|
|
341
|
-
|-------|---------|-------------|
|
|
342
|
-
| **Pico SQLAlchemy Repository** | `/pico-sqlalchemy` | Creates models, repositories and services with DI |
|
|
343
|
-
| **Pico Test Generator** | `/pico-tests` | Generates tests for pico-framework components |
|
|
373
|
+
All skills: `curl -sL https://raw.githubusercontent.com/dperezcabrera/pico-skills/main/install.sh | bash`
|
|
344
374
|
|
|
345
|
-
See [
|
|
375
|
+
See [pico-skills](https://github.com/dperezcabrera/pico-skills) for details.
|
|
346
376
|
|
|
347
377
|
---
|
|
348
378
|
|
|
349
|
-
##
|
|
379
|
+
## License
|
|
350
380
|
|
|
351
381
|
MIT
|