sqlalchemy-foundation-kit 0.0.0__tar.gz → 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.
Files changed (52) hide show
  1. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/CHANGELOG.md +12 -0
  2. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/PKG-INFO +3 -3
  3. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/README.md +1 -1
  4. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/__init__.py +3 -1
  5. sqlalchemy_foundation_kit-0.2.0/sqlalchemy_foundation_kit/__version__.py +1 -0
  6. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/uow/__init__.py +2 -1
  7. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/uow/protocols.py +18 -0
  8. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/uow/sqlalchemy.py +31 -0
  9. sqlalchemy_foundation_kit-0.0.0/sqlalchemy_foundation_kit/__version__.py +0 -1
  10. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/.gitignore +0 -0
  11. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/LICENSE +0 -0
  12. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/pyproject.toml +0 -0
  13. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/_typing.py +0 -0
  14. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/__init__.py +0 -0
  15. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/_optional.py +0 -0
  16. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/engine.py +0 -0
  17. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/metadata.py +0 -0
  18. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/models.py +0 -0
  19. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/serialization.py +0 -0
  20. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/base/types.py +0 -0
  21. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/config/__init__.py +0 -0
  22. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/config/postgres.py +0 -0
  23. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/__init__.py +0 -0
  24. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/_metrics_utils.py +0 -0
  25. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/dependency_injector/__init__.py +0 -0
  26. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/dependency_injector/_base.py +0 -0
  27. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/dependency_injector/_deps.py +0 -0
  28. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/dependency_injector/database.py +0 -0
  29. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/dependency_injector/metrics.py +0 -0
  30. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/di/__init__.py +0 -0
  31. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/di/_base.py +0 -0
  32. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/di/_deps.py +0 -0
  33. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/di/database.py +0 -0
  34. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/di/metrics.py +0 -0
  35. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/metrics/__init__.py +0 -0
  36. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/metrics/postgres.py +0 -0
  37. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/settings/__init__.py +0 -0
  38. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/settings/postgres.py +0 -0
  39. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/telemetry/__init__.py +0 -0
  40. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/telemetry/instrumentations.py +0 -0
  41. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/contrib/telemetry/uow.py +0 -0
  42. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/protocols/__init__.py +0 -0
  43. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/protocols/metrics.py +0 -0
  44. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/py.typed +0 -0
  45. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/__init__.py +0 -0
  46. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/builder.py +0 -0
  47. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/connection.py +0 -0
  48. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/factories.py +0 -0
  49. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/locks.py +0 -0
  50. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/manager.py +0 -0
  51. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/session/retry.py +0 -0
  52. {sqlalchemy_foundation_kit-0.0.0 → sqlalchemy_foundation_kit-0.2.0}/sqlalchemy_foundation_kit/uow/enums.py +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0](https://github.com/bedrock-python/sqlalchemy-foundation-kit/compare/sqlalchemy-foundation-kit-v0.1.0...sqlalchemy-foundation-kit-v0.2.0) (2026-08-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **uow:** add savepoint() to AsyncSQLAlchemyUowTransaction ([#6](https://github.com/bedrock-python/sqlalchemy-foundation-kit/issues/6)) ([c46d881](https://github.com/bedrock-python/sqlalchemy-foundation-kit/commit/c46d881052b7476211c31f4adc6058d4f5500b6a)), closes [#5](https://github.com/bedrock-python/sqlalchemy-foundation-kit/issues/5)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **ci:** add workflow_dispatch and created trigger to publish workflow ([142aef5](https://github.com/bedrock-python/sqlalchemy-foundation-kit/commit/142aef5040777b31509e8d7b26f01774e8f22432))
14
+
3
15
  ## 0.1.0 (2026-05-19)
4
16
 
5
17
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: sqlalchemy-foundation-kit
3
- Version: 0.0.0
3
+ Version: 0.2.0
4
4
  Summary: Foundation layer for SQLAlchemy-based services with UoW, session management, and observability
5
5
  Project-URL: Repository, https://github.com/bedrock-python/sqlalchemy-foundation-kit
6
6
  Project-URL: Documentation, https://bedrock-python.github.io/sqlalchemy-foundation-kit/
@@ -462,7 +462,7 @@ class CreateUserUseCase:
462
462
  ### Core (always available)
463
463
  - **Session Management**: `AsyncSessionManager`, `AsyncCConnection`
464
464
  - **Base ORM**: `Base`, `BaseTable`, `DatetimeColumnsMixin`, `UnConstrainedEnum`, `PydanticJSONB`
465
- - **Unit of Work**: `AsyncUnitOfWork`, `AsyncSQLAlchemyUnitOfWork`, `IsolationLevel`
465
+ - **Unit of Work**: `AsyncUnitOfWork`, `AsyncSQLAlchemyUnitOfWork`, `IsolationLevel`, `SupportsSavepoint`
466
466
  - **Protocols**: `PostgresSettingsProtocol`, `PostgresMetricsProtocol`
467
467
  - **Utilities**: `build_engine_kwargs`, `resolve_pool_class`, `load_orm_metadata`
468
468
 
@@ -212,7 +212,7 @@ class CreateUserUseCase:
212
212
  ### Core (always available)
213
213
  - **Session Management**: `AsyncSessionManager`, `AsyncCConnection`
214
214
  - **Base ORM**: `Base`, `BaseTable`, `DatetimeColumnsMixin`, `UnConstrainedEnum`, `PydanticJSONB`
215
- - **Unit of Work**: `AsyncUnitOfWork`, `AsyncSQLAlchemyUnitOfWork`, `IsolationLevel`
215
+ - **Unit of Work**: `AsyncUnitOfWork`, `AsyncSQLAlchemyUnitOfWork`, `IsolationLevel`, `SupportsSavepoint`
216
216
  - **Protocols**: `PostgresSettingsProtocol`, `PostgresMetricsProtocol`
217
217
  - **Utilities**: `build_engine_kwargs`, `resolve_pool_class`, `load_orm_metadata`
218
218
 
@@ -58,12 +58,13 @@ from .uow import (
58
58
  IsolationLevel,
59
59
  PostgresAdvisoryLockMixin,
60
60
  SupportsAdvisoryLock,
61
+ SupportsSavepoint,
61
62
  )
62
63
 
63
64
  try:
64
65
  __version__ = version("sqlalchemy-foundation-kit")
65
66
  except PackageNotFoundError: # pragma: no cover
66
- __version__ = "0.1.0"
67
+ __version__ = "0.2.0"
67
68
 
68
69
  __all__ = [ # noqa: RUF022
69
70
  # Base ORM
@@ -109,6 +110,7 @@ __all__ = [ # noqa: RUF022
109
110
  "IsolationLevel",
110
111
  "PostgresAdvisoryLockMixin",
111
112
  "SupportsAdvisoryLock",
113
+ "SupportsSavepoint",
112
114
  # Version
113
115
  "__version__",
114
116
  ]
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0" # x-release-please-version
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from .enums import IsolationLevel
6
- from .protocols import AsyncUnitOfWork, AsyncUowTransaction, SupportsAdvisoryLock
6
+ from .protocols import AsyncUnitOfWork, AsyncUowTransaction, SupportsAdvisoryLock, SupportsSavepoint
7
7
  from .sqlalchemy import (
8
8
  AsyncSQLAlchemyUnitOfWork,
9
9
  AsyncSQLAlchemyUowTransaction,
@@ -18,4 +18,5 @@ __all__ = [
18
18
  "IsolationLevel",
19
19
  "PostgresAdvisoryLockMixin",
20
20
  "SupportsAdvisoryLock",
21
+ "SupportsSavepoint",
21
22
  ]
@@ -32,6 +32,24 @@ class SupportsAdvisoryLock(Protocol):
32
32
  ...
33
33
 
34
34
 
35
+ class SupportsSavepoint(Protocol):
36
+ """Capability protocol for transactions supporting savepoints.
37
+
38
+ Use this when a use case must keep going after one step fails — for example,
39
+ processing a batch of independent items where a bad item must not take the
40
+ rest of the batch (or the record of its own failure) down with it.
41
+ """
42
+
43
+ def savepoint(self) -> AbstractAsyncContextManager[None]:
44
+ """Open a nested block whose failure does not poison the surrounding transaction.
45
+
46
+ On exception the block's changes are rolled back, the exception propagates
47
+ unchanged, and the surrounding transaction remains usable. On success the
48
+ block's changes stay part of the surrounding transaction.
49
+ """
50
+ ...
51
+
52
+
35
53
  class AsyncUnitOfWork(Protocol, Generic[T_co]):
36
54
  """Provides transactional context for repository operations.
37
55
 
@@ -129,6 +129,37 @@ class AsyncSQLAlchemyUowTransaction(AsyncUowTransaction):
129
129
  """Get the underlying SQLAlchemy async session."""
130
130
  return self._session
131
131
 
132
+ @asynccontextmanager
133
+ async def savepoint(self) -> AsyncIterator[None]:
134
+ """Open a savepoint whose failure does not poison the surrounding transaction.
135
+
136
+ On PostgreSQL a single failed statement aborts the whole transaction: every
137
+ later statement on the connection fails until rollback, including the ones
138
+ that would record which step failed. A savepoint (``SAVEPOINT`` /
139
+ ``ROLLBACK TO SAVEPOINT``) narrows that blast radius to this block.
140
+
141
+ On exception the block's changes are rolled back, the exception propagates
142
+ unchanged — the caller decides what a failed step means — and the surrounding
143
+ transaction stays usable. On success the savepoint is released and its changes
144
+ remain part of the surrounding transaction, committed or rolled back with it.
145
+ Savepoints nest: a ``savepoint()`` inside another rolls back only the inner block.
146
+
147
+ Intended for use inside :meth:`AsyncSQLAlchemyUnitOfWork.transaction` or
148
+ :meth:`AsyncSQLAlchemyUnitOfWork.managed_session`.
149
+
150
+ Example:
151
+ async with uow.transaction() as tx:
152
+ for event in await tx.outbox.list_pending():
153
+ try:
154
+ async with tx.savepoint():
155
+ await tx.outbox.process(event)
156
+ except Exception as exc:
157
+ await tx.outbox.mark_failed(event, reason=str(exc))
158
+ # Commits processed events and failure records together
159
+ """
160
+ async with self._session.begin_nested():
161
+ yield
162
+
132
163
 
133
164
  class PostgresAdvisoryLockMixin:
134
165
  """Mixin providing PostgreSQL advisory lock support for UoW transactions.
@@ -1 +0,0 @@
1
- __version__ = "0.0.0"