dclassql 0.1.2__tar.gz → 0.1.3__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.
- {dclassql-0.1.2 → dclassql-0.1.3}/PKG-INFO +1 -1
- {dclassql-0.1.2 → dclassql-0.1.3}/pyproject.toml +1 -1
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/__init__.py +2 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/client.py +22 -13
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/codegen.py +2 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/base.py +15 -20
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/protocols.py +11 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/sqlite.py +24 -10
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/client_module.py.jinja +0 -2
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/partials/imports.jinja +1 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/partials/model_section.jinja +4 -1
- dclassql-0.1.2/src/dclassql/templates/partials/datasource_config.jinja +0 -11
- {dclassql-0.1.2 → dclassql-0.1.3}/README.md +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/.gitignore +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/cli.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/db_pool.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/generated_models/__init__.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/generated_models/test_models.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/model_inspector.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/push/__init__.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/push/base.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/push/sqlite.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/__init__.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/lazy.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/backends/metadata.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/datasource.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/runtime/sqlite_adapters.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/table_spec.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/__init__.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/partials/client_class.jinja +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/partials/exports.jinja +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/templates/partials/macros.jinja +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/typing.py +0 -0
- {dclassql-0.1.2 → dclassql-0.1.3}/src/dclassql/unwarp.py +0 -0
|
@@ -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
|
]
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass
|
|
4
4
|
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
|
|
10
11
|
from dclassql.runtime.datasource import open_sqlite_connection
|
|
@@ -12,18 +13,6 @@ from dclassql.runtime.datasource import open_sqlite_connection
|
|
|
12
13
|
from datetime import datetime
|
|
13
14
|
from dclassql.generated_models.test_models import Address, BirthDay, Book, User, UserBook
|
|
14
15
|
|
|
15
|
-
@dataclass(slots=True)
|
|
16
|
-
class DataSourceConfig:
|
|
17
|
-
provider: str
|
|
18
|
-
url: str | None
|
|
19
|
-
name: str | None = None
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def key(self) -> str:
|
|
23
|
-
return self.name or self.provider
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
16
|
TAddressIncludeCol = Literal['user']
|
|
28
17
|
TAddressSortableCol = Literal['id', 'location', 'user_id']
|
|
29
18
|
|
|
@@ -57,6 +46,7 @@ class AddressOrderByDict(TypedDict, total=False, closed=True):
|
|
|
57
46
|
class AddressTable:
|
|
58
47
|
model = Address
|
|
59
48
|
insert_model = AddressInsert
|
|
49
|
+
table_name: str = 'Address'
|
|
60
50
|
datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
|
|
61
51
|
column_specs: tuple[ColumnSpec, ...] = (
|
|
62
52
|
ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
|
|
@@ -84,6 +74,9 @@ class AddressTable:
|
|
|
84
74
|
def __init__(self, backend: BackendProtocol) -> None:
|
|
85
75
|
self._backend = backend
|
|
86
76
|
|
|
77
|
+
def __str__(self) -> str:
|
|
78
|
+
return self._backend.escape_identifier(self.table_name)
|
|
79
|
+
|
|
87
80
|
def insert(self, data: AddressInsert | AddressInsertDict) -> Address:
|
|
88
81
|
return self._backend.insert(self, data)
|
|
89
82
|
|
|
@@ -133,6 +126,7 @@ class BirthDayOrderByDict(TypedDict, total=False, closed=True):
|
|
|
133
126
|
class BirthDayTable:
|
|
134
127
|
model = BirthDay
|
|
135
128
|
insert_model = BirthDayInsert
|
|
129
|
+
table_name: str = 'BirthDay'
|
|
136
130
|
datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
|
|
137
131
|
column_specs: tuple[ColumnSpec, ...] = (
|
|
138
132
|
ColumnSpec(name='user_id', optional=False, auto_increment=False, has_default=False, has_default_factory=False),
|
|
@@ -159,6 +153,9 @@ class BirthDayTable:
|
|
|
159
153
|
def __init__(self, backend: BackendProtocol) -> None:
|
|
160
154
|
self._backend = backend
|
|
161
155
|
|
|
156
|
+
def __str__(self) -> str:
|
|
157
|
+
return self._backend.escape_identifier(self.table_name)
|
|
158
|
+
|
|
162
159
|
def insert(self, data: BirthDayInsert | BirthDayInsertDict) -> BirthDay:
|
|
163
160
|
return self._backend.insert(self, data)
|
|
164
161
|
|
|
@@ -208,6 +205,7 @@ class BookOrderByDict(TypedDict, total=False, closed=True):
|
|
|
208
205
|
class BookTable:
|
|
209
206
|
model = Book
|
|
210
207
|
insert_model = BookInsert
|
|
208
|
+
table_name: str = 'Book'
|
|
211
209
|
datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
|
|
212
210
|
column_specs: tuple[ColumnSpec, ...] = (
|
|
213
211
|
ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
|
|
@@ -227,6 +225,9 @@ class BookTable:
|
|
|
227
225
|
def __init__(self, backend: BackendProtocol) -> None:
|
|
228
226
|
self._backend = backend
|
|
229
227
|
|
|
228
|
+
def __str__(self) -> str:
|
|
229
|
+
return self._backend.escape_identifier(self.table_name)
|
|
230
|
+
|
|
230
231
|
def insert(self, data: BookInsert | BookInsertDict) -> Book:
|
|
231
232
|
return self._backend.insert(self, data)
|
|
232
233
|
|
|
@@ -286,6 +287,7 @@ class UserOrderByDict(TypedDict, total=False, closed=True):
|
|
|
286
287
|
class UserTable:
|
|
287
288
|
model = User
|
|
288
289
|
insert_model = UserInsert
|
|
290
|
+
table_name: str = 'User'
|
|
289
291
|
datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
|
|
290
292
|
column_specs: tuple[ColumnSpec, ...] = (
|
|
291
293
|
ColumnSpec(name='id', optional=False, auto_increment=True, has_default=False, has_default_factory=False),
|
|
@@ -309,6 +311,9 @@ class UserTable:
|
|
|
309
311
|
def __init__(self, backend: BackendProtocol) -> None:
|
|
310
312
|
self._backend = backend
|
|
311
313
|
|
|
314
|
+
def __str__(self) -> str:
|
|
315
|
+
return self._backend.escape_identifier(self.table_name)
|
|
316
|
+
|
|
312
317
|
def insert(self, data: UserInsert | UserInsertDict) -> User:
|
|
313
318
|
return self._backend.insert(self, data)
|
|
314
319
|
|
|
@@ -363,6 +368,7 @@ class UserBookOrderByDict(TypedDict, total=False, closed=True):
|
|
|
363
368
|
class UserBookTable:
|
|
364
369
|
model = UserBook
|
|
365
370
|
insert_model = UserBookInsert
|
|
371
|
+
table_name: str = 'UserBook'
|
|
366
372
|
datasource = DataSourceConfig(provider='sqlite', url='sqlite:///test.db', name=None)
|
|
367
373
|
column_specs: tuple[ColumnSpec, ...] = (
|
|
368
374
|
ColumnSpec(name='user_id', optional=False, auto_increment=False, has_default=False, has_default_factory=False),
|
|
@@ -397,6 +403,9 @@ class UserBookTable:
|
|
|
397
403
|
def __init__(self, backend: BackendProtocol) -> None:
|
|
398
404
|
self._backend = backend
|
|
399
405
|
|
|
406
|
+
def __str__(self) -> str:
|
|
407
|
+
return self._backend.escape_identifier(self.table_name)
|
|
408
|
+
|
|
400
409
|
def insert(self, data: UserBookInsert | UserBookInsertDict) -> UserBook:
|
|
401
410
|
return self._backend.insert(self, data)
|
|
402
411
|
|
|
@@ -73,6 +73,7 @@ class RelationRender:
|
|
|
73
73
|
class ModelRenderContext:
|
|
74
74
|
name: str
|
|
75
75
|
datasource_expr: str
|
|
76
|
+
table_name_literal: str
|
|
76
77
|
insert_fields: tuple[InsertFieldSpec, ...]
|
|
77
78
|
typed_dict_fields: tuple[TypedDictFieldSpec, ...]
|
|
78
79
|
where_fields: tuple[WhereFieldSpec, ...]
|
|
@@ -253,6 +254,7 @@ def _build_model_context(
|
|
|
253
254
|
return ModelRenderContext(
|
|
254
255
|
name=name,
|
|
255
256
|
datasource_expr=datasource_expr,
|
|
257
|
+
table_name_literal=repr(name),
|
|
256
258
|
insert_fields=tuple(insert_fields),
|
|
257
259
|
typed_dict_fields=tuple(typed_dict_fields),
|
|
258
260
|
where_fields=tuple(where_fields),
|
|
@@ -49,17 +49,9 @@ class BackendBase(BackendProtocol, ABC):
|
|
|
49
49
|
sql = self._render_query(insert_query)
|
|
50
50
|
returning_columns = [spec.name for spec in table.column_specs]
|
|
51
51
|
sql_with_returning = self._append_returning(sql, returning_columns)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
row = cursor.fetchone()
|
|
57
|
-
if row is None:
|
|
58
|
-
raise RuntimeError("Inserted row not returned by backend")
|
|
59
|
-
if connection is not None:
|
|
60
|
-
connection.commit()
|
|
61
|
-
finally:
|
|
62
|
-
cursor.close()
|
|
52
|
+
|
|
53
|
+
row = self.query_raw(sql_with_returning, params, auto_commit=True)[0]
|
|
54
|
+
|
|
63
55
|
result = self._row_to_model(table, row, include_map={})
|
|
64
56
|
self._invalidate_backrefs(table, result)
|
|
65
57
|
return result
|
|
@@ -116,7 +108,7 @@ class BackendBase(BackendProtocol, ABC):
|
|
|
116
108
|
select_query = select_query.limit(take)
|
|
117
109
|
|
|
118
110
|
sql = self._render_query(select_query)
|
|
119
|
-
rows = self.
|
|
111
|
+
rows = self.query_raw(sql, params)
|
|
120
112
|
include_map = include or {}
|
|
121
113
|
return [self._row_to_model(table, row, include_map) for row in rows]
|
|
122
114
|
|
|
@@ -205,6 +197,17 @@ class BackendBase(BackendProtocol, ABC):
|
|
|
205
197
|
raise RuntimeError("Inserted row could not be reloaded")
|
|
206
198
|
return results[0]
|
|
207
199
|
|
|
200
|
+
def query_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = False) -> Sequence[object]:
|
|
201
|
+
raise NotImplementedError
|
|
202
|
+
|
|
203
|
+
def execute_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = True) -> int:
|
|
204
|
+
raise NotImplementedError
|
|
205
|
+
|
|
206
|
+
def escape_identifier(self, name: str) -> str:
|
|
207
|
+
if self.quote_char:
|
|
208
|
+
return format_quotes(name, self.quote_char)
|
|
209
|
+
raise ValueError("Backend does not support identifier quoting without a quote character set")
|
|
210
|
+
|
|
208
211
|
def _invalidate_backrefs(
|
|
209
212
|
self,
|
|
210
213
|
table: TableProtocol[ModelT, InsertT, WhereT, IncludeT, OrderByT],
|
|
@@ -313,11 +316,3 @@ class BackendBase(BackendProtocol, ABC):
|
|
|
313
316
|
else:
|
|
314
317
|
column_sql = ", ".join(columns)
|
|
315
318
|
return f"{trimmed} RETURNING {column_sql};"
|
|
316
|
-
|
|
317
|
-
@abstractmethod
|
|
318
|
-
def _fetch_all(self, sql: str, params: Sequence[Any]) -> Sequence[Any]:
|
|
319
|
-
raise NotImplementedError
|
|
320
|
-
|
|
321
|
-
@abstractmethod
|
|
322
|
-
def _execute(self, sql: str, params: Sequence[Any], *, auto_commit: bool = True) -> Any:
|
|
323
|
-
raise NotImplementedError
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import sqlite3
|
|
4
4
|
from typing import Callable, Literal, Mapping, Protocol, Sequence, runtime_checkable
|
|
5
5
|
|
|
6
|
+
from dclassql.model_inspector import DataSourceConfig
|
|
6
7
|
from dclassql.typing import IncludeT, InsertT, ModelT, OrderByT, WhereT
|
|
7
8
|
|
|
8
9
|
from .metadata import ColumnSpec, ForeignKeySpec, RelationSpec
|
|
@@ -21,9 +22,13 @@ class TableProtocol[
|
|
|
21
22
|
|
|
22
23
|
model: type[ModelT]
|
|
23
24
|
insert_model: type[InsertT]
|
|
25
|
+
table_name: str
|
|
26
|
+
datasource: DataSourceConfig
|
|
24
27
|
column_specs: tuple[ColumnSpec, ...]
|
|
25
28
|
column_specs_by_name: Mapping[str, ColumnSpec]
|
|
26
29
|
primary_key: tuple[str, ...]
|
|
30
|
+
indexes: tuple[tuple[str, ...], ...]
|
|
31
|
+
unique_indexes: tuple[tuple[str, ...], ...]
|
|
27
32
|
foreign_keys: tuple[ForeignKeySpec, ...]
|
|
28
33
|
relations: tuple[RelationSpec[BackendProtocol], ...]
|
|
29
34
|
|
|
@@ -64,3 +69,9 @@ class BackendProtocol(Protocol):
|
|
|
64
69
|
order_by: OrderByT | None = None,
|
|
65
70
|
skip: int | None = None,
|
|
66
71
|
) -> ModelT | None: ...
|
|
72
|
+
|
|
73
|
+
def query_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = False) -> Sequence[object]: ...
|
|
74
|
+
|
|
75
|
+
def execute_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = True) -> int: ...
|
|
76
|
+
|
|
77
|
+
def escape_identifier(self, name: str) -> str: ...
|
|
@@ -83,16 +83,6 @@ class SQLiteBackend(BackendBase):
|
|
|
83
83
|
results.append(instance)
|
|
84
84
|
start = end
|
|
85
85
|
return results
|
|
86
|
-
def _fetch_all(self, sql: str, params: Sequence[Any]) -> list[sqlite3.Row]:
|
|
87
|
-
cursor = self._execute(sql, params)
|
|
88
|
-
return cursor.fetchall()
|
|
89
|
-
|
|
90
|
-
def _execute(self, sql: str, params: Sequence[Any], *, auto_commit: bool = True) -> sqlite3.Cursor:
|
|
91
|
-
connection = self._acquire_connection()
|
|
92
|
-
cursor = connection.execute(sql, tuple(params))
|
|
93
|
-
if auto_commit:
|
|
94
|
-
connection.commit()
|
|
95
|
-
return cursor
|
|
96
86
|
|
|
97
87
|
def _acquire_connection(self) -> sqlite3.Connection:
|
|
98
88
|
if self._factory is None:
|
|
@@ -126,3 +116,27 @@ class SQLiteBackend(BackendBase):
|
|
|
126
116
|
connection.close()
|
|
127
117
|
delattr(self._local, "connection")
|
|
128
118
|
self._clear_identity_map()
|
|
119
|
+
|
|
120
|
+
def query_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = False) -> Sequence[object]:
|
|
121
|
+
connection = self._acquire_connection()
|
|
122
|
+
cursor = connection.execute(sql, params or ())
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
rows = cursor.fetchall()
|
|
126
|
+
|
|
127
|
+
if auto_commit:
|
|
128
|
+
connection.commit()
|
|
129
|
+
finally:
|
|
130
|
+
cursor.close()
|
|
131
|
+
return rows
|
|
132
|
+
|
|
133
|
+
def execute_raw(self, sql: str, params: Sequence[object] | None = None, auto_commit: bool = True) -> int:
|
|
134
|
+
connection = self._acquire_connection()
|
|
135
|
+
cursor = connection.execute(sql, params or ())
|
|
136
|
+
try:
|
|
137
|
+
affected = cursor.rowcount
|
|
138
|
+
if auto_commit:
|
|
139
|
+
connection.commit()
|
|
140
|
+
finally:
|
|
141
|
+
cursor.close()
|
|
142
|
+
return affected
|
|
@@ -5,6 +5,7 @@ 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
|
|
10
11
|
from dclassql.runtime.datasource import open_sqlite_connection
|
|
@@ -51,6 +51,7 @@ class {{ order_by_dict_class }}(TypedDict, total=False, closed=True):
|
|
|
51
51
|
class {{ table_class }}:
|
|
52
52
|
model = {{ name }}
|
|
53
53
|
insert_model = {{ insert_class }}
|
|
54
|
+
table_name: str = {{ model.table_name_literal }}
|
|
54
55
|
datasource = {{ model.datasource_expr }}
|
|
55
56
|
{{ macros.column_specs(model.column_specs)|indent(4, True) }}
|
|
56
57
|
column_specs_by_name: Mapping[str, ColumnSpec] = MappingProxyType({spec.name: spec for spec in column_specs})
|
|
@@ -63,6 +64,9 @@ class {{ table_class }}:
|
|
|
63
64
|
def __init__(self, backend: {{ backend_signature }}) -> None:
|
|
64
65
|
self._backend = backend
|
|
65
66
|
|
|
67
|
+
def __str__(self) -> str:
|
|
68
|
+
return self._backend.escape_identifier(self.table_name)
|
|
69
|
+
|
|
66
70
|
def insert(self, data: {{ insert_class }} | {{ insert_dict_class }}) -> {{ name }}:
|
|
67
71
|
return self._backend.insert(self, data)
|
|
68
72
|
|
|
@@ -82,4 +86,3 @@ class {{ table_class }}:
|
|
|
82
86
|
where=where, include=include, order_by=order_by,
|
|
83
87
|
skip=skip
|
|
84
88
|
)
|
|
85
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|