dclassql 0.1.2__tar.gz → 0.1.4__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 (40) hide show
  1. {dclassql-0.1.2 → dclassql-0.1.4}/PKG-INFO +1 -1
  2. {dclassql-0.1.2 → dclassql-0.1.4}/pyproject.toml +1 -1
  3. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/__init__.py +2 -0
  4. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/client.py +165 -40
  5. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/codegen.py +229 -4
  6. dclassql-0.1.4/src/dclassql/generated_models/exchange_info.py +345 -0
  7. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/model_inspector.py +3 -10
  8. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/push/sqlite.py +2 -2
  9. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/base.py +42 -31
  10. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/protocols.py +24 -8
  11. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/sqlite.py +64 -28
  12. dclassql-0.1.4/src/dclassql/runtime/backends/where_compiler.py +314 -0
  13. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/client_module.py.jinja +3 -1
  14. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/partials/client_class.jinja +7 -4
  15. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/partials/imports.jinja +2 -0
  16. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/partials/model_section.jinja +11 -2
  17. dclassql-0.1.4/src/dclassql/templates/partials/scalar_filters.jinja +5 -0
  18. dclassql-0.1.4/src/dclassql/typing.py +7 -0
  19. dclassql-0.1.4/src/dclassql/utils/__init__.py +1 -0
  20. dclassql-0.1.4/src/dclassql/utils/ensure.py +36 -0
  21. dclassql-0.1.2/src/dclassql/templates/partials/datasource_config.jinja +0 -11
  22. dclassql-0.1.2/src/dclassql/typing.py +0 -7
  23. {dclassql-0.1.2 → dclassql-0.1.4}/README.md +0 -0
  24. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/.gitignore +0 -0
  25. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/cli.py +0 -0
  26. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/db_pool.py +0 -0
  27. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/generated_models/__init__.py +0 -0
  28. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/generated_models/test_models.py +0 -0
  29. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/push/__init__.py +0 -0
  30. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/push/base.py +0 -0
  31. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/__init__.py +0 -0
  32. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/lazy.py +0 -0
  33. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/backends/metadata.py +0 -0
  34. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/datasource.py +0 -0
  35. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/runtime/sqlite_adapters.py +0 -0
  36. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/table_spec.py +0 -0
  37. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/__init__.py +0 -0
  38. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/partials/exports.jinja +0 -0
  39. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/templates/partials/macros.jinja +0 -0
  40. {dclassql-0.1.2 → dclassql-0.1.4}/src/dclassql/unwarp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dclassql
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A type-safe ORM generator for Python, creating fully type-hinted database clients from plain dataclass definitions.
5
5
  Keywords: orm,codegen,sqlite,dataclass,typed
6
6
  Author: myuanz
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dclassql"
3
- version = "0.1.2"
3
+ version = "0.1.4"
4
4
  description = "A type-safe ORM generator for Python, creating fully type-hinted database clients from plain dataclass definitions."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,3 +1,4 @@
1
+ from .model_inspector import DataSourceConfig
1
2
  from .db_pool import BaseDBPool, save_local
2
3
  from .push import db_push
3
4
  from .runtime.backends.lazy import eager
@@ -24,4 +25,5 @@ __all__ = [
24
25
  'unwarp_or_raise',
25
26
  'BaseDBPool',
26
27
  'save_local',
28
+ 'DataSourceConfig',
27
29
  ]
@@ -5,22 +5,50 @@ from types import MappingProxyType
5
5
  from typing import Any, Literal, Mapping, Sequence, NotRequired
6
6
  from typing_extensions import TypedDict
7
7
 
8
+ from dclassql import DataSourceConfig
8
9
  from dclassql.db_pool import BaseDBPool, save_local
9
10
  from dclassql.runtime.backends import BackendProtocol, ColumnSpec, ForeignKeySpec, RelationSpec
11
+ from dclassql.runtime.backends.protocols import TableProtocol
10
12
  from dclassql.runtime.datasource import open_sqlite_connection
11
13
 
12
14
  from datetime import datetime
13
15
  from dclassql.generated_models.test_models import Address, BirthDay, Book, User, UserBook
14
16
 
15
- @dataclass(slots=True)
16
- class DataSourceConfig:
17
- provider: str
18
- url: str | None
19
- name: str | None = None
17
+ class DateTimeFilter(TypedDict, total=False, closed=True):
18
+ EQ: datetime | None
19
+ IN: Sequence[datetime]
20
+ NOT_IN: Sequence[datetime]
21
+ LT: datetime
22
+ LTE: datetime
23
+ GT: datetime
24
+ GTE: datetime
25
+ NOT: DateTimeFilter | datetime | None
26
+
27
+
28
+ class IntFilter(TypedDict, total=False, closed=True):
29
+ EQ: int | None
30
+ IN: Sequence[int]
31
+ NOT_IN: Sequence[int]
32
+ LT: int
33
+ LTE: int
34
+ GT: int
35
+ GTE: int
36
+ NOT: IntFilter | int | None
37
+
38
+
39
+ class StringFilter(TypedDict, total=False, closed=True):
40
+ EQ: str | None
41
+ IN: Sequence[str]
42
+ NOT_IN: Sequence[str]
43
+ LT: str
44
+ LTE: str
45
+ GT: str
46
+ GTE: str
47
+ CONTAINS: str
48
+ STARTS_WITH: str
49
+ ENDS_WITH: str
50
+ NOT: StringFilter | str | None
20
51
 
21
- @property
22
- def key(self) -> str:
23
- return self.name or self.provider
24
52
 
25
53
 
26
54
 
@@ -40,10 +68,20 @@ class AddressInsertDict(TypedDict, closed=True):
40
68
  user_id: int
41
69
 
42
70
 
71
+ class AddressUserRelationFilter(TypedDict, total=False, closed=True):
72
+ IS: UserWhereDict | None
73
+ IS_NOT: UserWhereDict | None
74
+
75
+
76
+
43
77
  class AddressWhereDict(TypedDict, total=False, closed=True):
44
- id: int | None
45
- location: str | None
46
- user_id: int | None
78
+ id: int | None | IntFilter
79
+ location: str | None | StringFilter
80
+ user_id: int | None | IntFilter
81
+ user: AddressUserRelationFilter
82
+ AND: AddressWhereDict | Sequence[AddressWhereDict]
83
+ OR: Sequence[AddressWhereDict]
84
+ NOT: AddressWhereDict | Sequence[AddressWhereDict]
47
85
 
48
86
 
49
87
  class AddressIncludeDict(TypedDict, total=False, closed=True):
@@ -54,9 +92,10 @@ class AddressOrderByDict(TypedDict, total=False, closed=True):
54
92
  location: Literal['asc', 'desc']
55
93
  user_id: Literal['asc', 'desc']
56
94
 
57
- class AddressTable:
95
+ class AddressTable(TableProtocol):
58
96
  model = Address
59
97
  insert_model = AddressInsert
98
+ table_name: str = 'Address'
60
99
  datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
61
100
  column_specs: tuple[ColumnSpec, ...] = (
62
101
  ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
@@ -84,6 +123,9 @@ class AddressTable:
84
123
  def __init__(self, backend: BackendProtocol) -> None:
85
124
  self._backend = backend
86
125
 
126
+ def __str__(self) -> str:
127
+ return self._backend.escape_identifier(self.table_name)
128
+
87
129
  def insert(self, data: AddressInsert | AddressInsertDict) -> Address:
88
130
  return self._backend.insert(self, data)
89
131
 
@@ -103,7 +145,6 @@ class AddressTable:
103
145
  where=where, include=include, order_by=order_by,
104
146
  skip=skip
105
147
  )
106
-
107
148
  TBirthDayIncludeCol = Literal['user']
108
149
  TBirthDaySortableCol = Literal['user_id', 'date']
109
150
 
@@ -118,9 +159,19 @@ class BirthDayInsertDict(TypedDict, closed=True):
118
159
  date: datetime
119
160
 
120
161
 
162
+ class BirthDayUserRelationFilter(TypedDict, total=False, closed=True):
163
+ IS: UserWhereDict | None
164
+ IS_NOT: UserWhereDict | None
165
+
166
+
167
+
121
168
  class BirthDayWhereDict(TypedDict, total=False, closed=True):
122
- user_id: int | None
123
- date: datetime | None
169
+ user_id: int | None | IntFilter
170
+ date: datetime | None | DateTimeFilter
171
+ user: BirthDayUserRelationFilter
172
+ AND: BirthDayWhereDict | Sequence[BirthDayWhereDict]
173
+ OR: Sequence[BirthDayWhereDict]
174
+ NOT: BirthDayWhereDict | Sequence[BirthDayWhereDict]
124
175
 
125
176
 
126
177
  class BirthDayIncludeDict(TypedDict, total=False, closed=True):
@@ -130,9 +181,10 @@ class BirthDayOrderByDict(TypedDict, total=False, closed=True):
130
181
  user_id: Literal['asc', 'desc']
131
182
  date: Literal['asc', 'desc']
132
183
 
133
- class BirthDayTable:
184
+ class BirthDayTable(TableProtocol):
134
185
  model = BirthDay
135
186
  insert_model = BirthDayInsert
187
+ table_name: str = 'BirthDay'
136
188
  datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
137
189
  column_specs: tuple[ColumnSpec, ...] = (
138
190
  ColumnSpec(name='user_id', optional=False, auto_increment=False, has_default=False, has_default_factory=False),
@@ -159,6 +211,9 @@ class BirthDayTable:
159
211
  def __init__(self, backend: BackendProtocol) -> None:
160
212
  self._backend = backend
161
213
 
214
+ def __str__(self) -> str:
215
+ return self._backend.escape_identifier(self.table_name)
216
+
162
217
  def insert(self, data: BirthDayInsert | BirthDayInsertDict) -> BirthDay:
163
218
  return self._backend.insert(self, data)
164
219
 
@@ -178,7 +233,6 @@ class BirthDayTable:
178
233
  where=where, include=include, order_by=order_by,
179
234
  skip=skip
180
235
  )
181
-
182
236
  TBookIncludeCol = Literal['users']
183
237
  TBookSortableCol = Literal['id', 'name']
184
238
 
@@ -193,9 +247,20 @@ class BookInsertDict(TypedDict, closed=True):
193
247
  name: str
194
248
 
195
249
 
250
+ class BookUsersRelationFilter(TypedDict, total=False, closed=True):
251
+ SOME: UserBookWhereDict | None
252
+ NONE: UserBookWhereDict | None
253
+ EVERY: UserBookWhereDict
254
+
255
+
256
+
196
257
  class BookWhereDict(TypedDict, total=False, closed=True):
197
- id: int | None
198
- name: str | None
258
+ id: int | None | IntFilter
259
+ name: str | None | StringFilter
260
+ users: BookUsersRelationFilter
261
+ AND: BookWhereDict | Sequence[BookWhereDict]
262
+ OR: Sequence[BookWhereDict]
263
+ NOT: BookWhereDict | Sequence[BookWhereDict]
199
264
 
200
265
 
201
266
  class BookIncludeDict(TypedDict, total=False, closed=True):
@@ -205,9 +270,10 @@ class BookOrderByDict(TypedDict, total=False, closed=True):
205
270
  id: Literal['asc', 'desc']
206
271
  name: Literal['asc', 'desc']
207
272
 
208
- class BookTable:
273
+ class BookTable(TableProtocol):
209
274
  model = Book
210
275
  insert_model = BookInsert
276
+ table_name: str = 'Book'
211
277
  datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
212
278
  column_specs: tuple[ColumnSpec, ...] = (
213
279
  ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
@@ -227,6 +293,9 @@ class BookTable:
227
293
  def __init__(self, backend: BackendProtocol) -> None:
228
294
  self._backend = backend
229
295
 
296
+ def __str__(self) -> str:
297
+ return self._backend.escape_identifier(self.table_name)
298
+
230
299
  def insert(self, data: BookInsert | BookInsertDict) -> Book:
231
300
  return self._backend.insert(self, data)
232
301
 
@@ -246,7 +315,6 @@ class BookTable:
246
315
  where=where, include=include, order_by=order_by,
247
316
  skip=skip
248
317
  )
249
-
250
318
  TUserIncludeCol = Literal['addresses', 'birthday', 'books']
251
319
  TUserSortableCol = Literal['id', 'name', 'email', 'last_login']
252
320
 
@@ -265,11 +333,35 @@ class UserInsertDict(TypedDict, closed=True):
265
333
  last_login: datetime
266
334
 
267
335
 
336
+ class UserBirthdayRelationFilter(TypedDict, total=False, closed=True):
337
+ IS: BirthDayWhereDict | None
338
+ IS_NOT: BirthDayWhereDict | None
339
+
340
+
341
+ class UserAddressesRelationFilter(TypedDict, total=False, closed=True):
342
+ SOME: AddressWhereDict | None
343
+ NONE: AddressWhereDict | None
344
+ EVERY: AddressWhereDict
345
+
346
+
347
+ class UserBooksRelationFilter(TypedDict, total=False, closed=True):
348
+ SOME: UserBookWhereDict | None
349
+ NONE: UserBookWhereDict | None
350
+ EVERY: UserBookWhereDict
351
+
352
+
353
+
268
354
  class UserWhereDict(TypedDict, total=False, closed=True):
269
- id: int | None
270
- name: str | None
271
- email: str | None
272
- last_login: datetime | None
355
+ id: int | None | IntFilter
356
+ name: str | None | StringFilter
357
+ email: str | None | StringFilter
358
+ last_login: datetime | None | DateTimeFilter
359
+ birthday: UserBirthdayRelationFilter
360
+ addresses: UserAddressesRelationFilter
361
+ books: UserBooksRelationFilter
362
+ AND: UserWhereDict | Sequence[UserWhereDict]
363
+ OR: Sequence[UserWhereDict]
364
+ NOT: UserWhereDict | Sequence[UserWhereDict]
273
365
 
274
366
 
275
367
  class UserIncludeDict(TypedDict, total=False, closed=True):
@@ -283,9 +375,10 @@ class UserOrderByDict(TypedDict, total=False, closed=True):
283
375
  email: Literal['asc', 'desc']
284
376
  last_login: Literal['asc', 'desc']
285
377
 
286
- class UserTable:
378
+ class UserTable(TableProtocol):
287
379
  model = User
288
380
  insert_model = UserInsert
381
+ table_name: str = 'User'
289
382
  datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
290
383
  column_specs: tuple[ColumnSpec, ...] = (
291
384
  ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
@@ -309,6 +402,9 @@ class UserTable:
309
402
  def __init__(self, backend: BackendProtocol) -> None:
310
403
  self._backend = backend
311
404
 
405
+ def __str__(self) -> str:
406
+ return self._backend.escape_identifier(self.table_name)
407
+
312
408
  def insert(self, data: UserInsert | UserInsertDict) -> User:
313
409
  return self._backend.insert(self, data)
314
410
 
@@ -328,7 +424,6 @@ class UserTable:
328
424
  where=where, include=include, order_by=order_by,
329
425
  skip=skip
330
426
  )
331
-
332
427
  TUserBookIncludeCol = Literal['book', 'user']
333
428
  TUserBookSortableCol = Literal['user_id', 'book_id', 'created_at']
334
429
 
@@ -345,10 +440,26 @@ class UserBookInsertDict(TypedDict, closed=True):
345
440
  created_at: datetime
346
441
 
347
442
 
443
+ class UserBookUserRelationFilter(TypedDict, total=False, closed=True):
444
+ IS: UserWhereDict | None
445
+ IS_NOT: UserWhereDict | None
446
+
447
+
448
+ class UserBookBookRelationFilter(TypedDict, total=False, closed=True):
449
+ IS: BookWhereDict | None
450
+ IS_NOT: BookWhereDict | None
451
+
452
+
453
+
348
454
  class UserBookWhereDict(TypedDict, total=False, closed=True):
349
- user_id: int | None
350
- book_id: int | None
351
- created_at: datetime | None
455
+ user_id: int | None | IntFilter
456
+ book_id: int | None | IntFilter
457
+ created_at: datetime | None | DateTimeFilter
458
+ user: UserBookUserRelationFilter
459
+ book: UserBookBookRelationFilter
460
+ AND: UserBookWhereDict | Sequence[UserBookWhereDict]
461
+ OR: Sequence[UserBookWhereDict]
462
+ NOT: UserBookWhereDict | Sequence[UserBookWhereDict]
352
463
 
353
464
 
354
465
  class UserBookIncludeDict(TypedDict, total=False, closed=True):
@@ -360,9 +471,10 @@ class UserBookOrderByDict(TypedDict, total=False, closed=True):
360
471
  book_id: Literal['asc', 'desc']
361
472
  created_at: Literal['asc', 'desc']
362
473
 
363
- class UserBookTable:
474
+ class UserBookTable(TableProtocol):
364
475
  model = UserBook
365
476
  insert_model = UserBookInsert
477
+ table_name: str = 'UserBook'
366
478
  datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
367
479
  column_specs: tuple[ColumnSpec, ...] = (
368
480
  ColumnSpec(name='user_id', optional=False, auto_increment=False, has_default=False, has_default_factory=False),
@@ -397,6 +509,9 @@ class UserBookTable:
397
509
  def __init__(self, backend: BackendProtocol) -> None:
398
510
  self._backend = backend
399
511
 
512
+ def __str__(self) -> str:
513
+ return self._backend.escape_identifier(self.table_name)
514
+
400
515
  def insert(self, data: UserBookInsert | UserBookInsertDict) -> UserBook:
401
516
  return self._backend.insert(self, data)
402
517
 
@@ -416,29 +531,31 @@ class UserBookTable:
416
531
  where=where, include=include, order_by=order_by,
417
532
  skip=skip
418
533
  )
419
-
420
534
  class Client(BaseDBPool):
535
+ _echo_sql: bool = False
421
536
  datasources = {
422
537
  'sqlite': DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None),
423
538
  }
424
539
 
425
540
  @classmethod
426
541
  @save_local
427
- def _backend_sqlite(cls) -> BackendProtocol:
542
+ def _backend_sqlite(cls, *, echo_sql: bool | None = None) -> BackendProtocol:
428
543
  config = cls.datasources['sqlite']
544
+ backend_echo = cls._echo_sql if echo_sql is None else echo_sql
429
545
  if config.provider == 'sqlite':
430
546
  from dclassql.runtime.backends.sqlite import SQLiteBackend
431
547
  conn = open_sqlite_connection(config.url)
432
548
  cls._setup_sqlite_db(conn)
433
- return SQLiteBackend(conn)
549
+ return SQLiteBackend(conn, echo_sql=backend_echo)
434
550
  raise ValueError(f"Unsupported provider '{config.provider}' for datasource 'sqlite'")
435
551
 
436
- def __init__(self) -> None:
437
- self.address = AddressTable(self._backend_sqlite())
438
- self.birth_day = BirthDayTable(self._backend_sqlite())
439
- self.book = BookTable(self._backend_sqlite())
440
- self.user = UserTable(self._backend_sqlite())
441
- self.user_book = UserBookTable(self._backend_sqlite())
552
+ def __init__(self, *, echo_sql: bool = False) -> None:
553
+ self._echo_sql = echo_sql
554
+ self.address = AddressTable(self._backend_sqlite(echo_sql=echo_sql))
555
+ self.birth_day = BirthDayTable(self._backend_sqlite(echo_sql=echo_sql))
556
+ self.book = BookTable(self._backend_sqlite(echo_sql=echo_sql))
557
+ self.user = UserTable(self._backend_sqlite(echo_sql=echo_sql))
558
+ self.user_book = UserBookTable(self._backend_sqlite(echo_sql=echo_sql))
442
559
 
443
560
  @classmethod
444
561
  def close_all(cls, verbose: bool = False) -> None:
@@ -461,6 +578,7 @@ __all__ = (
461
578
  "AddressInsertDict",
462
579
  "AddressWhereDict",
463
580
  "AddressTable",
581
+ "AddressUserRelationFilter",
464
582
  "TBirthDayIncludeCol",
465
583
  "TBirthDaySortableCol",
466
584
  "BirthDayIncludeDict",
@@ -469,6 +587,7 @@ __all__ = (
469
587
  "BirthDayInsertDict",
470
588
  "BirthDayWhereDict",
471
589
  "BirthDayTable",
590
+ "BirthDayUserRelationFilter",
472
591
  "TBookIncludeCol",
473
592
  "TBookSortableCol",
474
593
  "BookIncludeDict",
@@ -477,6 +596,7 @@ __all__ = (
477
596
  "BookInsertDict",
478
597
  "BookWhereDict",
479
598
  "BookTable",
599
+ "BookUsersRelationFilter",
480
600
  "TUserIncludeCol",
481
601
  "TUserSortableCol",
482
602
  "UserIncludeDict",
@@ -485,6 +605,9 @@ __all__ = (
485
605
  "UserInsertDict",
486
606
  "UserWhereDict",
487
607
  "UserTable",
608
+ "UserBirthdayRelationFilter",
609
+ "UserAddressesRelationFilter",
610
+ "UserBooksRelationFilter",
488
611
  "TUserBookIncludeCol",
489
612
  "TUserBookSortableCol",
490
613
  "UserBookIncludeDict",
@@ -493,4 +616,6 @@ __all__ = (
493
616
  "UserBookInsertDict",
494
617
  "UserBookWhereDict",
495
618
  "UserBookTable",
619
+ "UserBookUserRelationFilter",
620
+ "UserBookBookRelationFilter",
496
621
  )