belgie-alchemy 0.1.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 (29) hide show
  1. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/PKG-INFO +43 -33
  2. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/README.md +39 -31
  3. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/pyproject.toml +7 -2
  4. belgie_alchemy-0.2.0/src/belgie_alchemy/__init__.py +7 -0
  5. belgie_alchemy-0.1.0/src/belgie_alchemy/__init__.py +0 -33
  6. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/__init__.py +0 -0
  7. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/adapter/__init__.py +0 -0
  8. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/adapter/test_adapter.py +0 -493
  9. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/auth_models/__init__.py +0 -0
  10. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/auth_models/test_auth_models.py +0 -91
  11. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/base/__init__.py +0 -0
  12. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/base/test_base.py +0 -90
  13. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/conftest.py +0 -39
  14. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/fixtures/__init__.py +0 -12
  15. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/fixtures/database.py +0 -38
  16. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/fixtures/models.py +0 -119
  17. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/mixins/__init__.py +0 -0
  18. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/mixins/test_mixins.py +0 -80
  19. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/settings/__init__.py +0 -0
  20. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/settings/test_settings.py +0 -342
  21. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/settings/test_settings_integration.py +0 -416
  22. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/types/__init__.py +0 -0
  23. belgie_alchemy-0.1.0/src/belgie_alchemy/__tests__/types/test_types.py +0 -155
  24. belgie_alchemy-0.1.0/src/belgie_alchemy/base.py +0 -25
  25. belgie_alchemy-0.1.0/src/belgie_alchemy/mixins.py +0 -83
  26. belgie_alchemy-0.1.0/src/belgie_alchemy/types.py +0 -32
  27. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/src/belgie_alchemy/adapter.py +0 -0
  28. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/src/belgie_alchemy/py.typed +0 -0
  29. {belgie_alchemy-0.1.0 → belgie_alchemy-0.2.0}/src/belgie_alchemy/settings.py +0 -0
@@ -1,38 +1,43 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: belgie-alchemy
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: SQLAlchemy building blocks for Belgie
5
5
  Author: Matt LeMay
6
6
  Author-email: Matt LeMay <mplemay@users.noreply.github.com>
7
+ Requires-Dist: aiosqlite>=0.22.1
7
8
  Requires-Dist: belgie-proto
9
+ Requires-Dist: brussels>=0.2.0
8
10
  Requires-Dist: pydantic>=2.0
9
11
  Requires-Dist: pydantic-settings>=2.0
10
12
  Requires-Dist: sqlalchemy[asyncio]>=2.0
11
- Requires-Python: >=3.12
13
+ Requires-Python: >=3.12, <3.15
12
14
  Description-Content-Type: text/markdown
13
15
 
14
16
  # belgie-alchemy
15
17
 
16
- SQLAlchemy 2.0 building blocks for database models.
18
+ SQLAlchemy 2.0 utilities for Belgie.
17
19
 
18
20
  ## Overview
19
21
 
20
- `belgie-alchemy` provides opinionated defaults and utilities for SQLAlchemy:
22
+ `belgie-alchemy` provides the `AlchemyAdapter` and database settings for Belgie.
23
+ For SQLAlchemy building blocks (Base, mixins, types), use `brussels`:
21
24
 
22
25
  - **Base**: Declarative base with dataclass mapping and sensible defaults
23
26
  - **Mixins**: `PrimaryKeyMixin` (UUID), `TimestampMixin` (created/updated/deleted timestamps)
24
- - **Types**: `DateTimeUTC` (timezone-aware datetimes), `Scopes` (dialect-specific array/JSON storage)
27
+ - **Types**: `DateTimeUTC` (timezone-aware datetimes), `Json` (dialect-specific JSON storage)
25
28
 
26
- This module provides **building blocks only** - you define your own models.
29
+ The examples below use `brussels` directly so you can own your models.
27
30
 
28
31
  ## Quick Start
29
32
 
30
33
  ```python
31
34
  from datetime import datetime
32
- from belgie_alchemy import Base, PrimaryKeyMixin, TimestampMixin, DateTimeUTC
35
+ from brussels.base import DataclassBase
36
+ from brussels.mixins import PrimaryKeyMixin, TimestampMixin
37
+ from brussels.types import DateTimeUTC
33
38
  from sqlalchemy.orm import Mapped, mapped_column
34
39
 
35
- class Article(Base, PrimaryKeyMixin, TimestampMixin):
40
+ class Article(DataclassBase, PrimaryKeyMixin, TimestampMixin):
36
41
  __tablename__ = "articles"
37
42
 
38
43
  title: Mapped[str]
@@ -53,10 +58,10 @@ This gives you:
53
58
  Declarative base with dataclass mapping enabled:
54
59
 
55
60
  ```python
56
- from belgie_alchemy import Base
61
+ from brussels.base import DataclassBase
57
62
  from sqlalchemy.orm import Mapped, mapped_column
58
63
 
59
- class MyModel(Base):
64
+ class MyModel(DataclassBase):
60
65
  __tablename__ = "my_models"
61
66
 
62
67
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -70,7 +75,7 @@ Features:
70
75
 
71
76
  - Consistent naming conventions for constraints
72
77
  - Automatic type annotation mapping (`datetime` → `DateTimeUTC`)
73
- - Dataclass mapping with `kw_only=True`, `repr=True`, `eq=True`
78
+ - Dataclass mapping for convenient instantiation
74
79
 
75
80
  ### Mixins
76
81
 
@@ -79,9 +84,10 @@ Features:
79
84
  Adds a UUID primary key with server-side generation:
80
85
 
81
86
  ```python
82
- from belgie_alchemy import Base, PrimaryKeyMixin
87
+ from brussels.base import DataclassBase
88
+ from brussels.mixins import PrimaryKeyMixin
83
89
 
84
- class MyModel(Base, PrimaryKeyMixin):
90
+ class MyModel(DataclassBase, PrimaryKeyMixin):
85
91
  __tablename__ = "my_models"
86
92
  # Automatically includes: id: Mapped[UUID]
87
93
  ```
@@ -98,9 +104,10 @@ The `id` field:
98
104
  Adds automatic timestamp tracking:
99
105
 
100
106
  ```python
101
- from belgie_alchemy import Base, TimestampMixin
107
+ from brussels.base import DataclassBase
108
+ from brussels.mixins import TimestampMixin
102
109
 
103
- class MyModel(Base, TimestampMixin):
110
+ class MyModel(DataclassBase, TimestampMixin):
104
111
  __tablename__ = "my_models"
105
112
  # Automatically includes:
106
113
  # - created_at: Mapped[datetime]
@@ -123,10 +130,11 @@ Timezone-aware datetime storage:
123
130
 
124
131
  ```python
125
132
  from datetime import datetime
126
- from belgie_alchemy import Base, DateTimeUTC
133
+ from brussels.base import DataclassBase
134
+ from brussels.types import DateTimeUTC
127
135
  from sqlalchemy.orm import Mapped, mapped_column
128
136
 
129
- class Event(Base):
137
+ class Event(DataclassBase):
130
138
  __tablename__ = "events"
131
139
 
132
140
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -140,43 +148,43 @@ Features:
140
148
  - Always returns UTC-aware datetimes from database
141
149
  - Works with PostgreSQL, SQLite, MySQL
142
150
 
143
- #### Scopes
151
+ #### Json
144
152
 
145
- Dialect-specific array storage for permission scopes:
153
+ Dialect-specific JSON storage (JSONB on PostgreSQL):
146
154
 
147
155
  ```python
148
- from enum import StrEnum
149
- from belgie_alchemy import Base, Scopes
156
+ from brussels.base import DataclassBase
157
+ from brussels.types import Json
150
158
  from sqlalchemy.orm import Mapped, mapped_column
151
159
 
152
- class User(Base):
160
+ class User(DataclassBase):
153
161
  __tablename__ = "users"
154
162
 
155
163
  id: Mapped[int] = mapped_column(primary_key=True)
156
- # Option 1: Simple string array (works everywhere)
157
- scopes: Mapped[list[str] | None] = mapped_column(Scopes, default=None)
164
+ # Store scopes as JSON (works everywhere)
165
+ scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)
158
166
  ```
159
167
 
160
168
  Features:
161
169
 
162
- - PostgreSQL: Uses native `ARRAY(String)` type
163
- - SQLite/MySQL: Uses JSON storage
164
- - Automatically converts StrEnum values to strings
165
- - Handles `None` values correctly
170
+ - PostgreSQL: Uses `JSONB`
171
+ - SQLite/MySQL: Uses `JSON`
166
172
 
167
173
  For PostgreSQL with application-specific enum types, you can override:
168
174
 
169
175
  ```python
170
176
  from enum import StrEnum
177
+ from brussels.base import DataclassBase
171
178
  from sqlalchemy import ARRAY
172
179
  from sqlalchemy.dialects.postgresql import ENUM
180
+ from sqlalchemy.orm import Mapped, mapped_column
173
181
 
174
182
  class AppScope(StrEnum):
175
183
  READ = "resource:read"
176
184
  WRITE = "resource:write"
177
185
  ADMIN = "admin"
178
186
 
179
- class User(Base):
187
+ class User(DataclassBase):
180
188
  __tablename__ = "users"
181
189
 
182
190
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -203,16 +211,18 @@ Example structure:
203
211
  ```python
204
212
  from datetime import datetime
205
213
  from uuid import UUID
214
+ from brussels.base import DataclassBase
215
+ from brussels.mixins import PrimaryKeyMixin, TimestampMixin
216
+ from brussels.types import DateTimeUTC, Json
206
217
  from sqlalchemy import ForeignKey
207
218
  from sqlalchemy.orm import Mapped, mapped_column, relationship
208
- from belgie_alchemy import Base, PrimaryKeyMixin, TimestampMixin, DateTimeUTC, Scopes
209
219
 
210
- class User(Base, PrimaryKeyMixin, TimestampMixin):
220
+ class User(DataclassBase, PrimaryKeyMixin, TimestampMixin):
211
221
  __tablename__ = "users"
212
222
 
213
223
  email: Mapped[str] = mapped_column(unique=True, index=True)
214
224
  email_verified: Mapped[bool] = mapped_column(default=False)
215
- scopes: Mapped[list[str] | None] = mapped_column(Scopes, default=None)
225
+ scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)
216
226
 
217
227
  accounts: Mapped[list["Account"]] = relationship(
218
228
  back_populates="user",
@@ -220,7 +230,7 @@ class User(Base, PrimaryKeyMixin, TimestampMixin):
220
230
  init=False,
221
231
  )
222
232
 
223
- class Account(Base, PrimaryKeyMixin, TimestampMixin):
233
+ class Account(DataclassBase, PrimaryKeyMixin, TimestampMixin):
224
234
  __tablename__ = "accounts"
225
235
 
226
236
  user_id: Mapped[UUID] = mapped_column(
@@ -1,25 +1,28 @@
1
1
  # belgie-alchemy
2
2
 
3
- SQLAlchemy 2.0 building blocks for database models.
3
+ SQLAlchemy 2.0 utilities for Belgie.
4
4
 
5
5
  ## Overview
6
6
 
7
- `belgie-alchemy` provides opinionated defaults and utilities for SQLAlchemy:
7
+ `belgie-alchemy` provides the `AlchemyAdapter` and database settings for Belgie.
8
+ For SQLAlchemy building blocks (Base, mixins, types), use `brussels`:
8
9
 
9
10
  - **Base**: Declarative base with dataclass mapping and sensible defaults
10
11
  - **Mixins**: `PrimaryKeyMixin` (UUID), `TimestampMixin` (created/updated/deleted timestamps)
11
- - **Types**: `DateTimeUTC` (timezone-aware datetimes), `Scopes` (dialect-specific array/JSON storage)
12
+ - **Types**: `DateTimeUTC` (timezone-aware datetimes), `Json` (dialect-specific JSON storage)
12
13
 
13
- This module provides **building blocks only** - you define your own models.
14
+ The examples below use `brussels` directly so you can own your models.
14
15
 
15
16
  ## Quick Start
16
17
 
17
18
  ```python
18
19
  from datetime import datetime
19
- from belgie_alchemy import Base, PrimaryKeyMixin, TimestampMixin, DateTimeUTC
20
+ from brussels.base import DataclassBase
21
+ from brussels.mixins import PrimaryKeyMixin, TimestampMixin
22
+ from brussels.types import DateTimeUTC
20
23
  from sqlalchemy.orm import Mapped, mapped_column
21
24
 
22
- class Article(Base, PrimaryKeyMixin, TimestampMixin):
25
+ class Article(DataclassBase, PrimaryKeyMixin, TimestampMixin):
23
26
  __tablename__ = "articles"
24
27
 
25
28
  title: Mapped[str]
@@ -40,10 +43,10 @@ This gives you:
40
43
  Declarative base with dataclass mapping enabled:
41
44
 
42
45
  ```python
43
- from belgie_alchemy import Base
46
+ from brussels.base import DataclassBase
44
47
  from sqlalchemy.orm import Mapped, mapped_column
45
48
 
46
- class MyModel(Base):
49
+ class MyModel(DataclassBase):
47
50
  __tablename__ = "my_models"
48
51
 
49
52
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -57,7 +60,7 @@ Features:
57
60
 
58
61
  - Consistent naming conventions for constraints
59
62
  - Automatic type annotation mapping (`datetime` → `DateTimeUTC`)
60
- - Dataclass mapping with `kw_only=True`, `repr=True`, `eq=True`
63
+ - Dataclass mapping for convenient instantiation
61
64
 
62
65
  ### Mixins
63
66
 
@@ -66,9 +69,10 @@ Features:
66
69
  Adds a UUID primary key with server-side generation:
67
70
 
68
71
  ```python
69
- from belgie_alchemy import Base, PrimaryKeyMixin
72
+ from brussels.base import DataclassBase
73
+ from brussels.mixins import PrimaryKeyMixin
70
74
 
71
- class MyModel(Base, PrimaryKeyMixin):
75
+ class MyModel(DataclassBase, PrimaryKeyMixin):
72
76
  __tablename__ = "my_models"
73
77
  # Automatically includes: id: Mapped[UUID]
74
78
  ```
@@ -85,9 +89,10 @@ The `id` field:
85
89
  Adds automatic timestamp tracking:
86
90
 
87
91
  ```python
88
- from belgie_alchemy import Base, TimestampMixin
92
+ from brussels.base import DataclassBase
93
+ from brussels.mixins import TimestampMixin
89
94
 
90
- class MyModel(Base, TimestampMixin):
95
+ class MyModel(DataclassBase, TimestampMixin):
91
96
  __tablename__ = "my_models"
92
97
  # Automatically includes:
93
98
  # - created_at: Mapped[datetime]
@@ -110,10 +115,11 @@ Timezone-aware datetime storage:
110
115
 
111
116
  ```python
112
117
  from datetime import datetime
113
- from belgie_alchemy import Base, DateTimeUTC
118
+ from brussels.base import DataclassBase
119
+ from brussels.types import DateTimeUTC
114
120
  from sqlalchemy.orm import Mapped, mapped_column
115
121
 
116
- class Event(Base):
122
+ class Event(DataclassBase):
117
123
  __tablename__ = "events"
118
124
 
119
125
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -127,43 +133,43 @@ Features:
127
133
  - Always returns UTC-aware datetimes from database
128
134
  - Works with PostgreSQL, SQLite, MySQL
129
135
 
130
- #### Scopes
136
+ #### Json
131
137
 
132
- Dialect-specific array storage for permission scopes:
138
+ Dialect-specific JSON storage (JSONB on PostgreSQL):
133
139
 
134
140
  ```python
135
- from enum import StrEnum
136
- from belgie_alchemy import Base, Scopes
141
+ from brussels.base import DataclassBase
142
+ from brussels.types import Json
137
143
  from sqlalchemy.orm import Mapped, mapped_column
138
144
 
139
- class User(Base):
145
+ class User(DataclassBase):
140
146
  __tablename__ = "users"
141
147
 
142
148
  id: Mapped[int] = mapped_column(primary_key=True)
143
- # Option 1: Simple string array (works everywhere)
144
- scopes: Mapped[list[str] | None] = mapped_column(Scopes, default=None)
149
+ # Store scopes as JSON (works everywhere)
150
+ scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)
145
151
  ```
146
152
 
147
153
  Features:
148
154
 
149
- - PostgreSQL: Uses native `ARRAY(String)` type
150
- - SQLite/MySQL: Uses JSON storage
151
- - Automatically converts StrEnum values to strings
152
- - Handles `None` values correctly
155
+ - PostgreSQL: Uses `JSONB`
156
+ - SQLite/MySQL: Uses `JSON`
153
157
 
154
158
  For PostgreSQL with application-specific enum types, you can override:
155
159
 
156
160
  ```python
157
161
  from enum import StrEnum
162
+ from brussels.base import DataclassBase
158
163
  from sqlalchemy import ARRAY
159
164
  from sqlalchemy.dialects.postgresql import ENUM
165
+ from sqlalchemy.orm import Mapped, mapped_column
160
166
 
161
167
  class AppScope(StrEnum):
162
168
  READ = "resource:read"
163
169
  WRITE = "resource:write"
164
170
  ADMIN = "admin"
165
171
 
166
- class User(Base):
172
+ class User(DataclassBase):
167
173
  __tablename__ = "users"
168
174
 
169
175
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -190,16 +196,18 @@ Example structure:
190
196
  ```python
191
197
  from datetime import datetime
192
198
  from uuid import UUID
199
+ from brussels.base import DataclassBase
200
+ from brussels.mixins import PrimaryKeyMixin, TimestampMixin
201
+ from brussels.types import DateTimeUTC, Json
193
202
  from sqlalchemy import ForeignKey
194
203
  from sqlalchemy.orm import Mapped, mapped_column, relationship
195
- from belgie_alchemy import Base, PrimaryKeyMixin, TimestampMixin, DateTimeUTC, Scopes
196
204
 
197
- class User(Base, PrimaryKeyMixin, TimestampMixin):
205
+ class User(DataclassBase, PrimaryKeyMixin, TimestampMixin):
198
206
  __tablename__ = "users"
199
207
 
200
208
  email: Mapped[str] = mapped_column(unique=True, index=True)
201
209
  email_verified: Mapped[bool] = mapped_column(default=False)
202
- scopes: Mapped[list[str] | None] = mapped_column(Scopes, default=None)
210
+ scopes: Mapped[list[str] | None] = mapped_column(Json, default=None)
203
211
 
204
212
  accounts: Mapped[list["Account"]] = relationship(
205
213
  back_populates="user",
@@ -207,7 +215,7 @@ class User(Base, PrimaryKeyMixin, TimestampMixin):
207
215
  init=False,
208
216
  )
209
217
 
210
- class Account(Base, PrimaryKeyMixin, TimestampMixin):
218
+ class Account(DataclassBase, PrimaryKeyMixin, TimestampMixin):
211
219
  __tablename__ = "accounts"
212
220
 
213
221
  user_id: Mapped[UUID] = mapped_column(
@@ -1,14 +1,16 @@
1
1
  [project]
2
2
  name = "belgie-alchemy"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "SQLAlchemy building blocks for Belgie"
5
5
  readme = "README.md"
6
6
  authors = [
7
7
  { name = "Matt LeMay", email = "mplemay@users.noreply.github.com" }
8
8
  ]
9
- requires-python = ">=3.12"
9
+ requires-python = ">=3.12,<3.15"
10
10
  dependencies = [
11
+ "aiosqlite>=0.22.1",
11
12
  "belgie-proto",
13
+ "brussels>=0.2.0",
12
14
  "pydantic>=2.0",
13
15
  "pydantic-settings>=2.0",
14
16
  "sqlalchemy[asyncio]>=2.0",
@@ -17,3 +19,6 @@ dependencies = [
17
19
  [build-system]
18
20
  requires = ["uv_build>=0.9.28,<0.10.0"]
19
21
  build-backend = "uv_build"
22
+
23
+ [tool.uv.build-backend]
24
+ source-exclude = ["**/__tests__/**"]
@@ -0,0 +1,7 @@
1
+ from belgie_alchemy.adapter import AlchemyAdapter
2
+ from belgie_alchemy.settings import DatabaseSettings
3
+
4
+ __all__ = [
5
+ "AlchemyAdapter",
6
+ "DatabaseSettings",
7
+ ]
@@ -1,33 +0,0 @@
1
- """SQLAlchemy 2.0 building blocks for database models.
2
-
3
- This module provides opinionated defaults and utilities for SQLAlchemy:
4
- - Base: Declarative base with dataclass mapping and sensible defaults
5
- - Mixins: PrimaryKeyMixin (UUID), TimestampMixin (created/updated/deleted)
6
- - Types: DateTimeUTC (timezone-aware datetime storage)
7
-
8
- Usage:
9
- from belgie_alchemy import Base, PrimaryKeyMixin, TimestampMixin, DateTimeUTC
10
-
11
- class MyModel(Base, PrimaryKeyMixin, TimestampMixin):
12
- __tablename__ = "my_models"
13
-
14
- name: Mapped[str]
15
- created_on: Mapped[datetime] = mapped_column(DateTimeUTC)
16
-
17
- For complete auth model examples, see examples/alchemy/auth_models.py
18
- """
19
-
20
- from belgie_alchemy.adapter import AlchemyAdapter
21
- from belgie_alchemy.base import Base
22
- from belgie_alchemy.mixins import PrimaryKeyMixin, TimestampMixin
23
- from belgie_alchemy.settings import DatabaseSettings
24
- from belgie_alchemy.types import DateTimeUTC
25
-
26
- __all__ = [
27
- "AlchemyAdapter",
28
- "Base",
29
- "DatabaseSettings",
30
- "DateTimeUTC",
31
- "PrimaryKeyMixin",
32
- "TimestampMixin",
33
- ]