sqlphilosophy 0.1.9__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.

Potentially problematic release.


This version of sqlphilosophy might be problematic. Click here for more details.

Files changed (33) hide show
  1. {sqlphilosophy-0.1.9/src/sqlphilosophy.egg-info → sqlphilosophy-0.2.0}/PKG-INFO +3 -1
  2. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/README.md +1 -0
  3. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/pyproject.toml +1 -0
  4. sqlphilosophy-0.2.0/src/sqlphilosophy/VERSION +1 -0
  5. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/aio/protocols.py +25 -19
  6. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/aio/repository.py +12 -9
  7. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sync/protocols.py +27 -19
  8. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sync/repository.py +13 -10
  9. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0/src/sqlphilosophy.egg-info}/PKG-INFO +3 -1
  10. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy.egg-info/requires.txt +1 -0
  11. sqlphilosophy-0.1.9/src/sqlphilosophy/VERSION +0 -1
  12. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/LICENSE +0 -0
  13. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/MANIFEST.in +0 -0
  14. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/setup.cfg +0 -0
  15. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/__init__.py +0 -0
  16. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/_repository_shared.py +0 -0
  17. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/aio/__init__.py +0 -0
  18. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/aio/query.py +0 -0
  19. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/audit/__init__.py +0 -0
  20. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/audit/context.py +0 -0
  21. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/audit/fields.py +0 -0
  22. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/audit/listener.py +0 -0
  23. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/audit/model.py +0 -0
  24. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/py.typed +0 -0
  25. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sorting.py +0 -0
  26. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sql.py +0 -0
  27. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sync/__init__.py +0 -0
  28. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/sync/query.py +0 -0
  29. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/trusted_sql.py +0 -0
  30. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy/types.py +0 -0
  31. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy.egg-info/SOURCES.txt +0 -0
  32. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy.egg-info/dependency_links.txt +0 -0
  33. {sqlphilosophy-0.1.9 → sqlphilosophy-0.2.0}/src/sqlphilosophy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlphilosophy
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Portable SQLAlchemy repository kit: sync and async CRUD, statement builders, sort/pagination, and SQL helpers.
5
5
  Author-email: Josh Martin <denverprogrammer@gmail.com>
6
6
  License-Expression: MIT
@@ -20,6 +20,7 @@ Classifier: Typing :: Typed
20
20
  Requires-Python: <4.0,>=3.12
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: servicephilosophy>=0.1.0
23
24
  Requires-Dist: sqlalchemy<3,>=2.0
24
25
  Provides-Extra: async
25
26
  Requires-Dist: greenlet>=3.0; extra == "async"
@@ -56,6 +57,7 @@ Portable SQLAlchemy repository kit: sync and async CRUD, fluent statement builde
56
57
  | [Repository guide](./docs/repository-guide.md) | Entry point: overview, transaction model, links to usage pages |
57
58
  | [Usage examples](./docs/usage/) | Focused sync/async code examples by feature area |
58
59
  | [Strongly typed repositories](./docs/usage/strongly-typed-repositories.md) | Typed subclasses, factories, protocols, and service patterns |
60
+ | [Service factory composition](./docs/usage/service-factory-composition.md) | Compose sqlPhilosophy SQL repos with servicePhilosophy business services |
59
61
  | [Before/after SQLAlchemy](./docs/usage/before-after-sqlalchemy.md) | Migration examples: direct SQLAlchemy vs repository-centered code |
60
62
  | [Feature matrix](./docs/feature-matrix.md) | Full sync/async capability map |
61
63
  | [Typed repository (sync)](./docs/examples/typed_repository_sync.py) | Runnable factory + domain repo example |
@@ -17,6 +17,7 @@ Portable SQLAlchemy repository kit: sync and async CRUD, fluent statement builde
17
17
  | [Repository guide](./docs/repository-guide.md) | Entry point: overview, transaction model, links to usage pages |
18
18
  | [Usage examples](./docs/usage/) | Focused sync/async code examples by feature area |
19
19
  | [Strongly typed repositories](./docs/usage/strongly-typed-repositories.md) | Typed subclasses, factories, protocols, and service patterns |
20
+ | [Service factory composition](./docs/usage/service-factory-composition.md) | Compose sqlPhilosophy SQL repos with servicePhilosophy business services |
20
21
  | [Before/after SQLAlchemy](./docs/usage/before-after-sqlalchemy.md) | Migration examples: direct SQLAlchemy vs repository-centered code |
21
22
  | [Feature matrix](./docs/feature-matrix.md) | Full sync/async capability map |
22
23
  | [Typed repository (sync)](./docs/examples/typed_repository_sync.py) | Runnable factory + domain repo example |
@@ -24,6 +24,7 @@ classifiers = [
24
24
  ]
25
25
  dynamic = ["version"]
26
26
  dependencies = [
27
+ "servicephilosophy>=0.1.0",
27
28
  "sqlalchemy>=2.0,<3",
28
29
  ]
29
30
 
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any, Protocol, TypeVar
8
8
  if TYPE_CHECKING:
9
9
  from sqlphilosophy.aio.repository import AsyncBaseRepository
10
10
 
11
+ from servicephilosophy import RepositoryFactoryProtocol, ServiceRepositoryProtocol
11
12
  from sqlalchemy.ext.asyncio import AsyncSession
12
13
  from sqlalchemy.orm import DeclarativeBase
13
14
 
@@ -18,14 +19,35 @@ T = TypeVar("T", bound=DeclarativeBase)
18
19
  R = TypeVar("R", bound="AsyncBaseRepository[Any, Any]")
19
20
 
20
21
 
21
- class AsyncBaseRepositoryProtocol[T: DeclarativeBase, U: AsyncRepositoryFactory | None](
22
+ class AsyncRepositoryFactory(RepositoryFactoryProtocol, Protocol):
23
+ """Async session-scoped factory for statement builders and entity repositories."""
24
+
25
+ def create_statement(self, model: type[T]) -> AsyncStatementQueryBuilder[T]:
26
+ """Return a fluent async read builder for ``model``."""
27
+ ...
28
+
29
+ def get_repository(self, repo_class: type[R]) -> R:
30
+ """Return a cached typed entity repository."""
31
+ ...
32
+
33
+ def repository(self, model: type[T]) -> AsyncBaseRepositoryProtocol[T, AsyncRepositoryFactory]:
34
+ """Return generic CRUD helpers for ``model`` (``AsyncBaseRepository``)."""
35
+ ...
36
+
37
+
38
+ class AsyncBaseRepositoryProtocol[T: DeclarativeBase, U: AsyncRepositoryFactory](
39
+ ServiceRepositoryProtocol[U],
22
40
  Protocol,
23
41
  ):
24
- """Generic read/write surface shared by sqlphilosophy ``AsyncBaseRepository[T]``."""
42
+ """Async SQL read/write surface for ``AsyncBaseRepository[T]``.
43
+
44
+ Factory access (``.factory``, ``.maybe_factory``, ``.has_factory``) is inherited
45
+ from ``ServiceRepositoryProtocol``; this protocol adds the mapped model, session,
46
+ and SQLAlchemy CRUD/query methods only.
47
+ """
25
48
 
26
49
  model: type[T]
27
50
  _session: AsyncSession
28
- _factory: U | None
29
51
 
30
52
  async def get(self, obj_id: PrimaryKey, load_relations: Any = None) -> T: ...
31
53
 
@@ -88,19 +110,3 @@ class AsyncBaseRepositoryProtocol[T: DeclarativeBase, U: AsyncRepositoryFactory
88
110
  values: RowMapping,
89
111
  params: RowMapping | None = None,
90
112
  ) -> int: ...
91
-
92
-
93
- class AsyncRepositoryFactory(Protocol):
94
- """Session-scoped factory for async statement builders and entity repositories."""
95
-
96
- def create_statement(self, model: type[T]) -> AsyncStatementQueryBuilder[T]:
97
- """Return a fluent async read builder for ``model``."""
98
- ...
99
-
100
- def get_repository(self, repo_class: type[R]) -> R:
101
- """Return a cached typed entity repository."""
102
- ...
103
-
104
- def repository(self, model: type[T]) -> AsyncBaseRepositoryProtocol[T, AsyncRepositoryFactory | None]:
105
- """Return generic CRUD helpers for ``model`` (``AsyncBaseRepository`` in Phobos)."""
106
- ...
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from collections.abc import Sequence
6
6
  from typing import Any, cast
7
7
 
8
+ from servicephilosophy import ServiceRepository
8
9
  from sqlalchemy import delete, func, select, update
9
10
  from sqlalchemy import inspect as sa_inspect
10
11
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -40,18 +41,18 @@ from sqlphilosophy.types import (
40
41
  LoadRelations = Sequence[LoaderOption]
41
42
 
42
43
 
43
- class AsyncBaseRepository[T: DeclarativeBase, U: AsyncRepositoryFactory | None]:
44
+ class AsyncBaseRepository[T: DeclarativeBase, U: AsyncRepositoryFactory](ServiceRepository[U]):
44
45
  """Async session-scoped CRUD helpers for a single mapped model."""
45
46
 
46
47
  def __init__(
47
48
  self,
48
49
  model: type[T],
49
50
  session: AsyncSession,
50
- factory: AsyncRepositoryFactory | None = None,
51
+ factory: U | None = None,
51
52
  ) -> None:
53
+ super().__init__(factory)
52
54
  self.model = model
53
55
  self._session = session
54
- self._factory = factory
55
56
  self._pk_column = require_single_column_primary_key(model, self.inspect_model(model))
56
57
 
57
58
  @classmethod
@@ -375,12 +376,14 @@ class AsyncBaseRepository[T: DeclarativeBase, U: AsyncRepositoryFactory | None]:
375
376
 
376
377
  def statement(self) -> AsyncStatementQueryBuilder[T]:
377
378
  """Return a fluent statement builder for reads on this model (default read path)."""
378
- if self._factory is not None:
379
- return self._factory.create_statement(self.model)
379
+ factory = self.maybe_factory
380
+ if factory is not None:
381
+ return factory.create_statement(self.model)
380
382
  return AsyncSqlAlchemyStatementBuilder(self._session, self.model)
381
383
 
382
384
  def for_repo[R: AsyncBaseRepository[Any, Any]](self, repo_class: type[R]) -> R:
383
- """Return a typed entity repository sharing this session and factory."""
384
- if self._factory is None:
385
- raise RuntimeError("for_repo() requires an AsyncRepositoryFactory")
386
- return cast(R, self._factory.get_repository(repo_class))
385
+ """Return a typed entity repository sharing this session and factory.
386
+
387
+ Raises ``FactoryRequiredError`` when no factory was configured.
388
+ """
389
+ return cast(R, self.factory.get_repository(repo_class))
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any, Protocol, TypeVar
8
8
  if TYPE_CHECKING:
9
9
  from sqlphilosophy.sync.repository import BaseRepository
10
10
 
11
+ from servicephilosophy import RepositoryFactoryProtocol, ServiceRepositoryProtocol
11
12
  from sqlalchemy.orm import DeclarativeBase, Session
12
13
 
13
14
  from sqlphilosophy.sync.query import StatementQueryBuilder
@@ -17,12 +18,35 @@ T = TypeVar("T", bound=DeclarativeBase)
17
18
  R = TypeVar("R", bound="BaseRepository[Any, Any]")
18
19
 
19
20
 
20
- class BaseRepositoryProtocol[T: DeclarativeBase, U: RepositoryFactory | None](Protocol):
21
- """Generic read/write surface shared by sqlphilosophy ``BaseRepository[T]``."""
21
+ class RepositoryFactory(RepositoryFactoryProtocol, Protocol):
22
+ """Sync session-scoped factory for statement builders and entity repositories."""
23
+
24
+ def create_statement(self, model: type[T]) -> StatementQueryBuilder[T]:
25
+ """Return a fluent read builder for ``model``."""
26
+ ...
27
+
28
+ def get_repository(self, repo_class: type[R]) -> R:
29
+ """Return a cached typed entity repository."""
30
+ ...
31
+
32
+ def repository(self, model: type[T]) -> BaseRepositoryProtocol[T, RepositoryFactory]:
33
+ """Return generic CRUD helpers for ``model`` (``BaseRepository``)."""
34
+ ...
35
+
36
+
37
+ class BaseRepositoryProtocol[T: DeclarativeBase, U: RepositoryFactory](
38
+ ServiceRepositoryProtocol[U],
39
+ Protocol,
40
+ ):
41
+ """SQL read/write surface for ``BaseRepository[T]``.
42
+
43
+ Factory access (``.factory``, ``.maybe_factory``, ``.has_factory``) is inherited
44
+ from ``ServiceRepositoryProtocol``; this protocol adds the mapped model, session,
45
+ and SQLAlchemy CRUD/query methods only.
46
+ """
22
47
 
23
48
  model: type[T]
24
49
  _session: Session
25
- _factory: U | None
26
50
 
27
51
  def get(self, obj_id: PrimaryKey, load_relations: Any = None) -> T: ...
28
52
 
@@ -85,19 +109,3 @@ class BaseRepositoryProtocol[T: DeclarativeBase, U: RepositoryFactory | None](Pr
85
109
  values: RowMapping,
86
110
  params: RowMapping | None = None,
87
111
  ) -> int: ...
88
-
89
-
90
- class RepositoryFactory(Protocol):
91
- """Session-scoped factory for statement builders and entity repositories."""
92
-
93
- def create_statement(self, model: type[T]) -> StatementQueryBuilder[T]:
94
- """Return a fluent read builder for ``model``."""
95
- ...
96
-
97
- def get_repository(self, repo_class: type[R]) -> R:
98
- """Return a cached typed entity repository."""
99
- ...
100
-
101
- def repository(self, model: type[T]) -> BaseRepositoryProtocol[T, RepositoryFactory | None]:
102
- """Return generic CRUD helpers for ``model`` (``BaseRepository`` in Phobos)."""
103
- ...
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from collections.abc import Sequence
6
6
  from typing import Any, cast
7
7
 
8
+ from servicephilosophy import ServiceRepository
8
9
  from sqlalchemy import delete, func, select, update
9
10
  from sqlalchemy import inspect as sa_inspect
10
11
  from sqlalchemy.orm import DeclarativeBase, Session
@@ -37,18 +38,18 @@ from sqlphilosophy.types import (
37
38
  LoadRelations = Sequence[LoaderOption]
38
39
 
39
40
 
40
- class BaseRepository[T: DeclarativeBase, U: RepositoryFactory | None]:
41
- """Session-scoped CRUD helpers for a single mapped model."""
41
+ class BaseRepository[T: DeclarativeBase, U: RepositoryFactory](ServiceRepository[U]):
42
+ """Sync session-scoped CRUD helpers for a single mapped model."""
42
43
 
43
44
  def __init__(
44
45
  self,
45
46
  model: type[T],
46
47
  session: Session,
47
- factory: RepositoryFactory | None = None,
48
+ factory: U | None = None,
48
49
  ) -> None:
50
+ super().__init__(factory)
49
51
  self.model = model
50
52
  self._session = session
51
- self._factory = factory
52
53
  self._pk_column = require_single_column_primary_key(model, self.inspect_model(model))
53
54
 
54
55
  @classmethod
@@ -335,12 +336,14 @@ class BaseRepository[T: DeclarativeBase, U: RepositoryFactory | None]:
335
336
 
336
337
  def statement(self) -> StatementQueryBuilder[T]:
337
338
  """Return a fluent statement builder for reads on this model (default read path)."""
338
- if self._factory is not None:
339
- return self._factory.create_statement(self.model)
339
+ factory = self.maybe_factory
340
+ if factory is not None:
341
+ return factory.create_statement(self.model)
340
342
  return SqlAlchemyStatementBuilder(self._session, self.model)
341
343
 
342
344
  def for_repo[R: BaseRepository[Any, Any]](self, repo_class: type[R]) -> R:
343
- """Return a typed entity repository sharing this session and factory."""
344
- if self._factory is None:
345
- raise RuntimeError("for_repo() requires a RepositoryFactory")
346
- return cast(R, self._factory.get_repository(repo_class))
345
+ """Return a typed entity repository sharing this session and factory.
346
+
347
+ Raises ``FactoryRequiredError`` when no factory was configured.
348
+ """
349
+ return cast(R, self.factory.get_repository(repo_class))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlphilosophy
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Portable SQLAlchemy repository kit: sync and async CRUD, statement builders, sort/pagination, and SQL helpers.
5
5
  Author-email: Josh Martin <denverprogrammer@gmail.com>
6
6
  License-Expression: MIT
@@ -20,6 +20,7 @@ Classifier: Typing :: Typed
20
20
  Requires-Python: <4.0,>=3.12
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
+ Requires-Dist: servicephilosophy>=0.1.0
23
24
  Requires-Dist: sqlalchemy<3,>=2.0
24
25
  Provides-Extra: async
25
26
  Requires-Dist: greenlet>=3.0; extra == "async"
@@ -56,6 +57,7 @@ Portable SQLAlchemy repository kit: sync and async CRUD, fluent statement builde
56
57
  | [Repository guide](./docs/repository-guide.md) | Entry point: overview, transaction model, links to usage pages |
57
58
  | [Usage examples](./docs/usage/) | Focused sync/async code examples by feature area |
58
59
  | [Strongly typed repositories](./docs/usage/strongly-typed-repositories.md) | Typed subclasses, factories, protocols, and service patterns |
60
+ | [Service factory composition](./docs/usage/service-factory-composition.md) | Compose sqlPhilosophy SQL repos with servicePhilosophy business services |
59
61
  | [Before/after SQLAlchemy](./docs/usage/before-after-sqlalchemy.md) | Migration examples: direct SQLAlchemy vs repository-centered code |
60
62
  | [Feature matrix](./docs/feature-matrix.md) | Full sync/async capability map |
61
63
  | [Typed repository (sync)](./docs/examples/typed_repository_sync.py) | Runnable factory + domain repo example |
@@ -1,3 +1,4 @@
1
+ servicephilosophy>=0.1.0
1
2
  sqlalchemy<3,>=2.0
2
3
 
3
4
  [async]
@@ -1 +0,0 @@
1
- 0.1.9
File without changes
File without changes
File without changes