sqlphilosophy 0.1.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.
Files changed (44) hide show
  1. sqlphilosophy-0.1.0/LICENSE +21 -0
  2. sqlphilosophy-0.1.0/PKG-INFO +134 -0
  3. sqlphilosophy-0.1.0/README.md +99 -0
  4. sqlphilosophy-0.1.0/pyproject.toml +74 -0
  5. sqlphilosophy-0.1.0/setup.cfg +4 -0
  6. sqlphilosophy-0.1.0/src/sqlphilosophy/VERSION +1 -0
  7. sqlphilosophy-0.1.0/src/sqlphilosophy/__init__.py +3 -0
  8. sqlphilosophy-0.1.0/src/sqlphilosophy/aio/__init__.py +3 -0
  9. sqlphilosophy-0.1.0/src/sqlphilosophy/aio/protocols.py +26 -0
  10. sqlphilosophy-0.1.0/src/sqlphilosophy/aio/query.py +396 -0
  11. sqlphilosophy-0.1.0/src/sqlphilosophy/aio/repository.py +400 -0
  12. sqlphilosophy-0.1.0/src/sqlphilosophy/audit/__init__.py +3 -0
  13. sqlphilosophy-0.1.0/src/sqlphilosophy/audit/context.py +37 -0
  14. sqlphilosophy-0.1.0/src/sqlphilosophy/audit/fields.py +24 -0
  15. sqlphilosophy-0.1.0/src/sqlphilosophy/audit/listener.py +99 -0
  16. sqlphilosophy-0.1.0/src/sqlphilosophy/audit/model.py +59 -0
  17. sqlphilosophy-0.1.0/src/sqlphilosophy/py.typed +0 -0
  18. sqlphilosophy-0.1.0/src/sqlphilosophy/sorting.py +97 -0
  19. sqlphilosophy-0.1.0/src/sqlphilosophy/sql.py +532 -0
  20. sqlphilosophy-0.1.0/src/sqlphilosophy/sync/__init__.py +3 -0
  21. sqlphilosophy-0.1.0/src/sqlphilosophy/sync/protocols.py +26 -0
  22. sqlphilosophy-0.1.0/src/sqlphilosophy/sync/query.py +392 -0
  23. sqlphilosophy-0.1.0/src/sqlphilosophy/sync/repository.py +360 -0
  24. sqlphilosophy-0.1.0/src/sqlphilosophy/types.py +61 -0
  25. sqlphilosophy-0.1.0/src/sqlphilosophy.egg-info/PKG-INFO +134 -0
  26. sqlphilosophy-0.1.0/src/sqlphilosophy.egg-info/SOURCES.txt +42 -0
  27. sqlphilosophy-0.1.0/src/sqlphilosophy.egg-info/dependency_links.txt +1 -0
  28. sqlphilosophy-0.1.0/src/sqlphilosophy.egg-info/requires.txt +13 -0
  29. sqlphilosophy-0.1.0/src/sqlphilosophy.egg-info/top_level.txt +1 -0
  30. sqlphilosophy-0.1.0/tests/test_async_last_mile.py +98 -0
  31. sqlphilosophy-0.1.0/tests/test_async_query.py +74 -0
  32. sqlphilosophy-0.1.0/tests/test_async_repository.py +111 -0
  33. sqlphilosophy-0.1.0/tests/test_audit.py +99 -0
  34. sqlphilosophy-0.1.0/tests/test_close_coverage.py +125 -0
  35. sqlphilosophy-0.1.0/tests/test_coverage_gaps.py +351 -0
  36. sqlphilosophy-0.1.0/tests/test_final_coverage.py +191 -0
  37. sqlphilosophy-0.1.0/tests/test_import_contract.py +59 -0
  38. sqlphilosophy-0.1.0/tests/test_last_mile.py +87 -0
  39. sqlphilosophy-0.1.0/tests/test_package_boundaries.py +43 -0
  40. sqlphilosophy-0.1.0/tests/test_sorting.py +73 -0
  41. sqlphilosophy-0.1.0/tests/test_sql_helpers.py +221 -0
  42. sqlphilosophy-0.1.0/tests/test_sql_row_edges.py +66 -0
  43. sqlphilosophy-0.1.0/tests/test_sync_query.py +161 -0
  44. sqlphilosophy-0.1.0/tests/test_sync_repository.py +159 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Josh Martin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlphilosophy
3
+ Version: 0.1.0
4
+ Summary: Portable SQLAlchemy repository kit: sync and async CRUD, statement builders, sort/pagination, and SQL helpers.
5
+ Author-email: Josh Martin <denverprogrammer@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/SignalSafeSoftware/sqlphilosophy
8
+ Project-URL: Repository, https://github.com/SignalSafeSoftware/sqlphilosophy
9
+ Project-URL: Documentation, https://github.com/SignalSafeSoftware/sqlphilosophy#readme
10
+ Project-URL: Issues, https://github.com/SignalSafeSoftware/sqlphilosophy/issues
11
+ Project-URL: Changelog, https://github.com/SignalSafeSoftware/sqlphilosophy/blob/main/CHANGELOG.md
12
+ Keywords: sqlalchemy,repository-pattern,repository,orm,database,pagination,audit,async
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Database
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: <4.0,>=3.12
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: sqlalchemy<3,>=2.0
24
+ Provides-Extra: async
25
+ Requires-Dist: greenlet>=3.0; extra == "async"
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
29
+ Requires-Dist: pytest-cov>=5; extra == "dev"
30
+ Requires-Dist: aiosqlite>=0.20; extra == "dev"
31
+ Requires-Dist: greenlet>=3.0; extra == "dev"
32
+ Requires-Dist: build>=1.2; extra == "dev"
33
+ Requires-Dist: twine>=6.1; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # sqlphilosophy
37
+
38
+ Portable SQLAlchemy repository kit for typed CRUD, fluent statement building, sort/pagination, and Core SQL helpers — with explicit sync and async session APIs.
39
+
40
+ | | |
41
+ |---|---|
42
+ | **PyPI** | [`sqlphilosophy`](https://pypi.org/project/sqlphilosophy/) |
43
+ | **GitHub** | [SignalSafeSoftware/sqlphilosophy](https://github.com/SignalSafeSoftware/sqlphilosophy) |
44
+ | **Import** | `sqlphilosophy` (no root reexports — use explicit submodules below) |
45
+
46
+ Developed in the [DeliveryPlus](https://github.com/SignalSafeSoftware/DeliveryPlus) monorepo under `libs/sqlphilosophy`; this tree is the publishable package source.
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install sqlphilosophy
52
+ ```
53
+
54
+ Async ORM (`AsyncSession`) also needs greenlet:
55
+
56
+ ```bash
57
+ pip install sqlphilosophy[async]
58
+ ```
59
+
60
+ Requires Python 3.12+ and SQLAlchemy 2.x.
61
+
62
+ ## Package layout
63
+
64
+ | Module | Contents |
65
+ |--------|----------|
66
+ | `sqlphilosophy.types` | Portable typing aliases (`RowMapping`, `PrimaryKey`, `SqlFilter`, …) |
67
+ | `sqlphilosophy.sql` | Row mapping helpers, partial updates, Core table helpers, filter builders |
68
+ | `sqlphilosophy.sorting` | `ListQuery`, `SortConfig`, `SortSpec`, pagination/sort resolution |
69
+ | `sqlphilosophy.sync` | Sync `BaseRepository`, `StatementQueryBuilder`, `RepositoryFactory` protocol |
70
+ | `sqlphilosophy.aio` | Async `AsyncBaseRepository`, `AsyncStatementQueryBuilder`, `AsyncRepositoryFactory` |
71
+ | `sqlphilosophy.audit` | Optional SQLAlchemy audit listeners and timestamp mixins |
72
+
73
+ ## Sync usage
74
+
75
+ ```python
76
+ from sqlalchemy.orm import Session
77
+
78
+ from sqlphilosophy.sorting import ListQuery, SortConfig, SortSpec
79
+ from sqlphilosophy.sql import partial_update_model, row_int
80
+ from sqlphilosophy.sync.protocols import RepositoryFactory
81
+ from sqlphilosophy.sync.repository import BaseRepository
82
+ from sqlphilosophy.sync.query import SqlAlchemyStatementBuilder
83
+
84
+ # Without a factory — statement() returns SqlAlchemyStatementBuilder directly
85
+ repo = BaseRepository(User, session)
86
+ rows = repo.statement().where(User.active.is_(True)).mappings().all()
87
+
88
+ # With a factory — statement() and for_repo() delegate to the factory
89
+ repo = BaseRepository(User, session, factory)
90
+ page = repo.statement().fetch_page(ListQuery.from_page(page=1, size=20))
91
+ other = repo.for_repo(OrderRepository)
92
+ ```
93
+
94
+ ## Async usage
95
+
96
+ ```python
97
+ from sqlalchemy.ext.asyncio import AsyncSession
98
+
99
+ from sqlphilosophy.aio.repository import AsyncBaseRepository
100
+
101
+ repo = AsyncBaseRepository(User, session)
102
+ rows = await repo.statement().where(User.active.is_(True)).mappings().all()
103
+ ```
104
+
105
+ ## Audit mixins
106
+
107
+ ```python
108
+ from sqlphilosophy.audit.context import audit_context
109
+ from sqlphilosophy.audit.listener import configure_audit_listeners
110
+ from sqlphilosophy.audit.model import TimestampModel
111
+
112
+ configure_audit_listeners()
113
+
114
+ with audit_context(actor_id=42):
115
+ session.add(MyModel(name="example"))
116
+ session.flush()
117
+ ```
118
+
119
+ ## Development
120
+
121
+ ```bash
122
+ python -m pip install -e ".[dev]"
123
+ python -m pytest
124
+ python -m build
125
+ python -m twine check dist/*
126
+ ```
127
+
128
+ ## Releasing
129
+
130
+ See [RELEASING.md](./RELEASING.md) for GitHub + PyPI trusted publishing.
131
+
132
+ ## License
133
+
134
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,99 @@
1
+ # sqlphilosophy
2
+
3
+ Portable SQLAlchemy repository kit for typed CRUD, fluent statement building, sort/pagination, and Core SQL helpers — with explicit sync and async session APIs.
4
+
5
+ | | |
6
+ |---|---|
7
+ | **PyPI** | [`sqlphilosophy`](https://pypi.org/project/sqlphilosophy/) |
8
+ | **GitHub** | [SignalSafeSoftware/sqlphilosophy](https://github.com/SignalSafeSoftware/sqlphilosophy) |
9
+ | **Import** | `sqlphilosophy` (no root reexports — use explicit submodules below) |
10
+
11
+ Developed in the [DeliveryPlus](https://github.com/SignalSafeSoftware/DeliveryPlus) monorepo under `libs/sqlphilosophy`; this tree is the publishable package source.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install sqlphilosophy
17
+ ```
18
+
19
+ Async ORM (`AsyncSession`) also needs greenlet:
20
+
21
+ ```bash
22
+ pip install sqlphilosophy[async]
23
+ ```
24
+
25
+ Requires Python 3.12+ and SQLAlchemy 2.x.
26
+
27
+ ## Package layout
28
+
29
+ | Module | Contents |
30
+ |--------|----------|
31
+ | `sqlphilosophy.types` | Portable typing aliases (`RowMapping`, `PrimaryKey`, `SqlFilter`, …) |
32
+ | `sqlphilosophy.sql` | Row mapping helpers, partial updates, Core table helpers, filter builders |
33
+ | `sqlphilosophy.sorting` | `ListQuery`, `SortConfig`, `SortSpec`, pagination/sort resolution |
34
+ | `sqlphilosophy.sync` | Sync `BaseRepository`, `StatementQueryBuilder`, `RepositoryFactory` protocol |
35
+ | `sqlphilosophy.aio` | Async `AsyncBaseRepository`, `AsyncStatementQueryBuilder`, `AsyncRepositoryFactory` |
36
+ | `sqlphilosophy.audit` | Optional SQLAlchemy audit listeners and timestamp mixins |
37
+
38
+ ## Sync usage
39
+
40
+ ```python
41
+ from sqlalchemy.orm import Session
42
+
43
+ from sqlphilosophy.sorting import ListQuery, SortConfig, SortSpec
44
+ from sqlphilosophy.sql import partial_update_model, row_int
45
+ from sqlphilosophy.sync.protocols import RepositoryFactory
46
+ from sqlphilosophy.sync.repository import BaseRepository
47
+ from sqlphilosophy.sync.query import SqlAlchemyStatementBuilder
48
+
49
+ # Without a factory — statement() returns SqlAlchemyStatementBuilder directly
50
+ repo = BaseRepository(User, session)
51
+ rows = repo.statement().where(User.active.is_(True)).mappings().all()
52
+
53
+ # With a factory — statement() and for_repo() delegate to the factory
54
+ repo = BaseRepository(User, session, factory)
55
+ page = repo.statement().fetch_page(ListQuery.from_page(page=1, size=20))
56
+ other = repo.for_repo(OrderRepository)
57
+ ```
58
+
59
+ ## Async usage
60
+
61
+ ```python
62
+ from sqlalchemy.ext.asyncio import AsyncSession
63
+
64
+ from sqlphilosophy.aio.repository import AsyncBaseRepository
65
+
66
+ repo = AsyncBaseRepository(User, session)
67
+ rows = await repo.statement().where(User.active.is_(True)).mappings().all()
68
+ ```
69
+
70
+ ## Audit mixins
71
+
72
+ ```python
73
+ from sqlphilosophy.audit.context import audit_context
74
+ from sqlphilosophy.audit.listener import configure_audit_listeners
75
+ from sqlphilosophy.audit.model import TimestampModel
76
+
77
+ configure_audit_listeners()
78
+
79
+ with audit_context(actor_id=42):
80
+ session.add(MyModel(name="example"))
81
+ session.flush()
82
+ ```
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ python -m pip install -e ".[dev]"
88
+ python -m pytest
89
+ python -m build
90
+ python -m twine check dist/*
91
+ ```
92
+
93
+ ## Releasing
94
+
95
+ See [RELEASING.md](./RELEASING.md) for GitHub + PyPI trusted publishing.
96
+
97
+ ## License
98
+
99
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,74 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sqlphilosophy"
7
+ description = "Portable SQLAlchemy repository kit: sync and async CRUD, statement builders, sort/pagination, and SQL helpers."
8
+ readme = "README.md"
9
+ requires-python = ">=3.12,<4.0"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [
13
+ { name = "Josh Martin", email = "denverprogrammer@gmail.com" }
14
+ ]
15
+ keywords = ["sqlalchemy", "repository-pattern", "repository", "orm", "database", "pagination", "audit", "async"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Database",
23
+ "Typing :: Typed",
24
+ ]
25
+ dynamic = ["version"]
26
+ dependencies = [
27
+ "sqlalchemy>=2.0,<3",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ async = [
32
+ "greenlet>=3.0",
33
+ ]
34
+ dev = [
35
+ "pytest>=8",
36
+ "pytest-asyncio>=0.24",
37
+ "pytest-cov>=5",
38
+ "aiosqlite>=0.20",
39
+ "greenlet>=3.0",
40
+ "build>=1.2",
41
+ "twine>=6.1",
42
+ ]
43
+
44
+ [project.urls]
45
+ Homepage = "https://github.com/SignalSafeSoftware/sqlphilosophy"
46
+ Repository = "https://github.com/SignalSafeSoftware/sqlphilosophy"
47
+ Documentation = "https://github.com/SignalSafeSoftware/sqlphilosophy#readme"
48
+ Issues = "https://github.com/SignalSafeSoftware/sqlphilosophy/issues"
49
+ Changelog = "https://github.com/SignalSafeSoftware/sqlphilosophy/blob/main/CHANGELOG.md"
50
+
51
+ [tool.setuptools.packages.find]
52
+ where = ["src"]
53
+
54
+ [tool.setuptools.dynamic]
55
+ version = { file = ["src/sqlphilosophy/VERSION"] }
56
+
57
+ [tool.setuptools.package-data]
58
+ sqlphilosophy = ["py.typed", "VERSION"]
59
+
60
+ [tool.pytest.ini_options]
61
+ asyncio_mode = "auto"
62
+ testpaths = ["tests"]
63
+ addopts = "--cov=sqlphilosophy --cov-report=term-missing --cov-fail-under=100"
64
+
65
+ [tool.coverage.run]
66
+ source = ["sqlphilosophy"]
67
+ branch = false
68
+
69
+ [tool.coverage.report]
70
+ fail_under = 100
71
+ show_missing = true
72
+ exclude_also = [
73
+ "raise NotImplementedError",
74
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,3 @@
1
+ """SQLAlchemy repository kit — sync/async CRUD, query builders, sort, and SQL helpers."""
2
+
3
+ __all__: list[str] = []
@@ -0,0 +1,3 @@
1
+ """Async repository subpackage."""
2
+
3
+ __all__: list[str] = []
@@ -0,0 +1,26 @@
1
+ """Portable async repository factory protocol (no Phobos or app imports)."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Protocol
5
+ from typing import TypeVar
6
+ from sqlalchemy.orm import DeclarativeBase
7
+ from sqlphilosophy.aio.query import AsyncStatementQueryBuilder
8
+
9
+ T = TypeVar("T", bound=DeclarativeBase)
10
+ R = TypeVar("R")
11
+
12
+
13
+ class AsyncRepositoryFactory(Protocol):
14
+ """Session-scoped factory for async statement builders and entity repositories."""
15
+
16
+ def create_statement(self, model: type[T]) -> AsyncStatementQueryBuilder[T]:
17
+ """Return a fluent async read builder for ``model``."""
18
+ ...
19
+
20
+ def get_repository(self, repo_class: type[R]) -> R:
21
+ """Return a cached typed entity repository."""
22
+ ...
23
+
24
+ def repository(self, model: type[T]) -> object:
25
+ """Return generic CRUD helpers for ``model`` (``AsyncBaseRepository`` in Phobos)."""
26
+ ...