ormlambda 2.9.0__py3-none-any.whl → 2.9.4__py3-none-any.whl
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.
- ormlambda/common/abstract_classes/abstract_model.py +25 -11
- ormlambda/common/abstract_classes/decomposition_query.py +106 -91
- ormlambda/common/errors/__init__.py +3 -0
- ormlambda/common/interfaces/ICustomAlias.py +4 -0
- ormlambda/common/interfaces/IDecompositionQuery.py +5 -1
- ormlambda/common/interfaces/IRepositoryBase.py +1 -1
- ormlambda/common/interfaces/IStatements.py +97 -42
- ormlambda/databases/my_sql/clauses/alias.py +31 -0
- ormlambda/databases/my_sql/clauses/group_by.py +2 -2
- ormlambda/databases/my_sql/clauses/joins.py +39 -1
- ormlambda/databases/my_sql/clauses/select.py +12 -9
- ormlambda/databases/my_sql/repository.py +36 -25
- ormlambda/databases/my_sql/statements.py +52 -37
- ormlambda/model_base.py +3 -3
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/METADATA +1 -1
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/RECORD +18 -15
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/LICENSE +0 -0
- {ormlambda-2.9.0.dist-info → ormlambda-2.9.4.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
from typing import Any, Type, override, Iterable, Literal, TYPE_CHECKING
|
2
|
+
from typing import Any, Type, override, Iterable, Literal, TYPE_CHECKING, Optional
|
3
3
|
from collections import defaultdict
|
4
4
|
import abc
|
5
5
|
|
@@ -16,14 +16,16 @@ if TYPE_CHECKING:
|
|
16
16
|
ORDER_QUERIES = Literal["select", "join", "where", "order", "with", "group by", "limit", "offset"]
|
17
17
|
|
18
18
|
|
19
|
-
class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
19
|
+
class AbstractSQLStatements[T: Table, *Ts, TRepo](IStatements_two_generic[T, *Ts, TRepo]):
|
20
20
|
__slots__ = ("_model", "_repository", "_query_list")
|
21
21
|
__order__: tuple[str, ...] = ("select", "join", "where", "order", "with", "group by", "limit", "offset")
|
22
22
|
|
23
|
-
def __init__(self, model: T, repository: IRepositoryBase[TRepo]) -> None:
|
23
|
+
def __init__(self, model: tuple[T, *Ts], repository: IRepositoryBase[TRepo]) -> None:
|
24
24
|
self.__valid_repository(repository)
|
25
25
|
|
26
|
-
self.
|
26
|
+
self._query: Optional[str] = None
|
27
|
+
self._model: T = model[0] if isinstance(model, Iterable) else model
|
28
|
+
self._models: tuple[T, *Ts] = self._model if isinstance(model, Iterable) else (model,)
|
27
29
|
self._repository: IRepositoryBase[TRepo] = repository
|
28
30
|
self._query_list: dict[ORDER_QUERIES, list[IQuery]] = defaultdict(list)
|
29
31
|
|
@@ -41,12 +43,8 @@ class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
41
43
|
def __repr__(self):
|
42
44
|
return f"<Model: {self.__class__.__name__}>"
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
def repository(self) -> IRepositoryBase[TRepo]: ...
|
47
|
-
|
48
|
-
def _return_flavour[TValue](self, query, flavour: Type[TValue], select) -> tuple[TValue]:
|
49
|
-
return self._repository.read_sql(query, flavour=flavour, model=self._model, select=select)
|
46
|
+
def _return_flavour[TValue](self, query, flavour: Type[TValue], select, **kwargs) -> tuple[TValue]:
|
47
|
+
return self._repository.read_sql(query, flavour=flavour, model=self._model, select=select, **kwargs)
|
50
48
|
|
51
49
|
def _return_model(self, select, query: str):
|
52
50
|
response_sql = self._repository.read_sql(query, flavour=dict, model=self._model, select=select) # store all columns of the SQL query
|
@@ -59,6 +57,22 @@ class AbstractSQLStatements[T: Table, TRepo](IStatements_two_generic[T, TRepo]):
|
|
59
57
|
@abc.abstractmethod
|
60
58
|
def _build(sef): ...
|
61
59
|
|
60
|
+
@property
|
61
|
+
def query(self) -> str:
|
62
|
+
return self._query
|
63
|
+
|
64
|
+
@property
|
65
|
+
def model(self) -> Type[T]:
|
66
|
+
return self._model
|
67
|
+
|
68
|
+
@property
|
69
|
+
def models(self) -> tuple[*Ts]:
|
70
|
+
return self._models
|
71
|
+
|
72
|
+
@property
|
73
|
+
@override
|
74
|
+
def repository(self) -> IRepositoryBase[TRepo]: ...
|
75
|
+
|
62
76
|
|
63
77
|
class ClusterQuery[T]:
|
64
78
|
def __init__(self, select: DecompositionQueryBase[T], response_sql: tuple[dict[str, Any]]) -> None:
|
@@ -82,7 +96,7 @@ class ClusterQuery[T]:
|
|
82
96
|
for dicc_cols in self._response_sql:
|
83
97
|
valid_attr: dict[str, Any] = {}
|
84
98
|
for clause in clauses:
|
85
|
-
if not hasattr(table, clause.column):
|
99
|
+
if clause.column is None or not hasattr(table, clause.column):
|
86
100
|
agg_methods = self.__get_all_aggregate_method(clauses)
|
87
101
|
raise ValueError(f"You cannot use aggregation method like '{agg_methods}' to return model objects. Try specifying 'flavour' attribute as 'dict'.")
|
88
102
|
valid_attr[clause.column] = dicc_cols[clause.alias]
|
@@ -5,13 +5,15 @@ import inspect
|
|
5
5
|
import abc
|
6
6
|
from ormlambda import Table
|
7
7
|
|
8
|
-
from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction
|
8
|
+
from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction, TupleInstruction, NestedElement
|
9
9
|
from ormlambda.common.interfaces import IAggregate, IDecompositionQuery
|
10
10
|
from ormlambda import JoinType, ForeignKey
|
11
11
|
from ormlambda.databases.my_sql.clauses.joins import JoinSelector
|
12
|
-
from ormlambda.utils.module_tree.dfs_traversal import DFSTraversal
|
13
12
|
|
14
|
-
|
13
|
+
from ..errors import DifferentTablesAndVariablesError
|
14
|
+
|
15
|
+
type ClauseDataType = property | str
|
16
|
+
type AliasType[T] = tp.Type[Table] | tp.Callable[[tp.Type[Table]], T]
|
15
17
|
|
16
18
|
|
17
19
|
class ClauseInfo[T: tp.Type[Table]]:
|
@@ -60,7 +62,7 @@ class ClauseInfo[T: tp.Type[Table]]:
|
|
60
62
|
|
61
63
|
elif isinstance(data, str):
|
62
64
|
# TODOL: refactor to base the condition in dict with '*' as key. '*' must to work as special character
|
63
|
-
return f"'{data}'" if data !=
|
65
|
+
return f"'{data}'" if data != DecompositionQueryBase.CHAR else data
|
64
66
|
|
65
67
|
def __create_value_string(self, name: str) -> str:
|
66
68
|
if isinstance(self._row_column, property):
|
@@ -79,38 +81,47 @@ class ClauseInfo[T: tp.Type[Table]]:
|
|
79
81
|
return f"{column_name} as `{alias}`"
|
80
82
|
|
81
83
|
|
82
|
-
class DecompositionQueryBase[T: tp.Type[Table]](IDecompositionQuery[T]):
|
84
|
+
class DecompositionQueryBase[T: tp.Type[Table], *Ts](IDecompositionQuery[T, *Ts]):
|
85
|
+
CHAR: str = "*"
|
86
|
+
|
87
|
+
@staticmethod
|
88
|
+
def _asterik_resolver(table: tp.Type[Table]):
|
89
|
+
return table
|
90
|
+
|
83
91
|
@tp.overload
|
84
|
-
def __init__
|
92
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple]) -> None: ...
|
85
93
|
@tp.overload
|
86
|
-
def __init__
|
94
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ...) -> None: ...
|
87
95
|
@tp.overload
|
88
|
-
def __init__
|
96
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ...) -> None: ...
|
89
97
|
@tp.overload
|
90
|
-
def __init__
|
98
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ...) -> None: ...
|
91
99
|
@tp.overload
|
92
|
-
def __init__
|
100
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ..., replace_asterisk_char: bool = ...) -> None: ...
|
101
|
+
@tp.overload
|
102
|
+
def __init__(self, tables: T, lambda_query: tp.Callable[[T], tuple], *, alias: bool = ..., alias_name: tp.Optional[str] = ..., by: JoinType = ..., replace_asterisk_char: bool = ..., joins: tp.Optional[list[JoinSelector]] = ...) -> None: ...
|
93
103
|
|
94
|
-
def __init__
|
104
|
+
def __init__(
|
95
105
|
self,
|
96
|
-
|
106
|
+
tables: tuple[T, *Ts],
|
97
107
|
lambda_query: tp.Callable[[T], tuple[*Ts]],
|
98
108
|
*,
|
99
109
|
alias: bool = True,
|
100
110
|
alias_name: tp.Optional[str] = None,
|
101
111
|
by: JoinType = JoinType.INNER_JOIN,
|
102
112
|
replace_asterisk_char: bool = True,
|
113
|
+
joins: tp.Optional[list[JoinType]] = None,
|
103
114
|
) -> None:
|
104
|
-
self.
|
105
|
-
self._lambda_query: tp.Callable[[T], tuple
|
115
|
+
self._tables: tuple[T, *Ts] = tables if isinstance(tables, tp.Iterable) else (tables,)
|
116
|
+
self._lambda_query: tp.Callable[[T], tuple] = lambda_query
|
106
117
|
self._alias: bool = alias
|
107
118
|
self._alias_name: tp.Optional[str] = alias_name
|
108
119
|
self._by: JoinType = by
|
120
|
+
self._joins: set[JoinSelector] = set(joins) if joins is not None else set()
|
109
121
|
|
110
|
-
self._fk_relationship: set[tuple[tp.Type[Table], tp.Type[Table]]] = set()
|
111
122
|
self._clauses_group_by_tables: dict[tp.Type[Table], list[ClauseInfo[T]]] = defaultdict(list)
|
112
123
|
self._all_clauses: list[ClauseInfo] = []
|
113
|
-
self.alias_cache: dict[str,
|
124
|
+
self.alias_cache: dict[str, AliasType] = {self.CHAR: self._asterik_resolver}
|
114
125
|
self._replace_asterisk_char: bool = replace_asterisk_char
|
115
126
|
self.__assign_lambda_variables_to_table(lambda_query)
|
116
127
|
|
@@ -143,77 +154,92 @@ class DecompositionQueryBase[T: tp.Type[Table]](IDecompositionQuery[T]):
|
|
143
154
|
"""
|
144
155
|
lambda_vars = tuple(inspect.signature(_lambda).parameters)
|
145
156
|
|
146
|
-
|
147
|
-
|
157
|
+
# COMMENT: We don't pass a lambda method because lambda reads the las value of 'i'
|
158
|
+
for i, param in enumerate(lambda_vars):
|
159
|
+
self.alias_cache[param] = self._tables[i]
|
148
160
|
return None
|
149
161
|
|
150
162
|
def __clauses_list_generetor(self, function: tp.Callable[[T], tp.Any]) -> None:
|
151
163
|
if not callable(function):
|
152
164
|
return None
|
165
|
+
try:
|
166
|
+
resolved_function = function(*self._tables)
|
167
|
+
except TypeError:
|
168
|
+
raise DifferentTablesAndVariablesError
|
153
169
|
|
154
|
-
|
155
|
-
|
170
|
+
tree_list = TreeInstruction(function).to_list()
|
156
171
|
# Python treats string objects as iterable, so we need to prevent this behavior
|
157
172
|
if isinstance(resolved_function, str) or not isinstance(resolved_function, tp.Iterable):
|
158
173
|
resolved_function = (resolved_function,)
|
159
174
|
|
160
|
-
for
|
161
|
-
|
175
|
+
for col_index, last_data in enumerate(resolved_function):
|
176
|
+
ti = tree_list[col_index] if tree_list else TupleInstruction(self.CHAR, NestedElement(self.CHAR))
|
177
|
+
|
178
|
+
values: ClauseInfo | list[ClauseInfo] = self.__identify_value_type(last_data, ti)
|
162
179
|
|
163
180
|
if isinstance(values, tp.Iterable):
|
164
|
-
[self.
|
181
|
+
[self.__add_clause(x) for x in values]
|
165
182
|
else:
|
166
|
-
self.
|
183
|
+
self.__add_clause(values)
|
167
184
|
|
168
185
|
return None
|
169
186
|
|
170
|
-
def
|
187
|
+
def __identify_value_type[TProp](self, last_data: TProp, tuple_instruction: TupleInstruction) -> ClauseInfo[T]:
|
171
188
|
"""
|
172
189
|
A method that behaves based on the variable's type
|
173
190
|
"""
|
174
|
-
if isinstance(value, property):
|
175
|
-
if value in self._table.__properties_mapped__:
|
176
|
-
return ClauseInfo[T](self._table, value, self.alias_children_resolver)
|
177
191
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
self.
|
192
|
+
if isinstance(last_data, property):
|
193
|
+
if tuple_instruction.var == self.CHAR:
|
194
|
+
table_left = self.table
|
195
|
+
else:
|
196
|
+
table_left = self.alias_cache[tuple_instruction.var]
|
197
|
+
|
198
|
+
if last_data in table_left.__properties_mapped__:
|
199
|
+
# if self.table != table_left:
|
200
|
+
# self._add_fk_relationship(self.table, table_left)
|
201
|
+
return ClauseInfo[T](table_left, last_data, self.alias_children_resolver)
|
202
|
+
|
203
|
+
for table in self.tables:
|
204
|
+
try:
|
205
|
+
return self._search_correct_table_for_prop(table, tuple_instruction, last_data)
|
206
|
+
except ValueError:
|
207
|
+
continue
|
208
|
+
|
209
|
+
elif isinstance(last_data, IAggregate):
|
210
|
+
return ClauseInfo[T](self.table, last_data, self.alias_children_resolver)
|
211
|
+
|
212
|
+
# if value is a Table instance (when you need to retrieve all columns) we'll ensure that all JOINs are added
|
213
|
+
elif isinstance(last_data, type) and issubclass(last_data, Table):
|
214
|
+
if last_data not in self._tables:
|
215
|
+
self.__add_necessary_fk(tuple_instruction, last_data)
|
187
216
|
# all columns
|
188
217
|
clauses: list[ClauseInfo] = []
|
189
|
-
for prop in
|
218
|
+
for prop in last_data.__properties_mapped__:
|
190
219
|
if isinstance(prop, property):
|
191
|
-
clauses.append(self.
|
220
|
+
clauses.append(self.__identify_value_type(prop, tuple_instruction))
|
192
221
|
return clauses
|
193
222
|
|
194
|
-
elif isinstance(
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
223
|
+
elif isinstance(last_data, str):
|
224
|
+
# COMMENT: use self.table instead self._tables because if we hit this conditional, means that
|
225
|
+
# COMMENT: alias_cache to replace '*' by all columns
|
226
|
+
if self._replace_asterisk_char and (replace_value := self.alias_cache.get(last_data, None)) is not None:
|
227
|
+
return self.__identify_value_type(replace_value(self.table), tuple_instruction)
|
228
|
+
return ClauseInfo[T](self.table, last_data, alias_children_resolver=self.alias_children_resolver)
|
199
229
|
|
200
|
-
|
201
|
-
...
|
230
|
+
raise NotImplementedError(f"type of value '{last_data}' is not implemented.")
|
202
231
|
|
203
|
-
|
232
|
+
def _search_correct_table_for_prop[TTable](self, table: tp.Type[Table], tuple_instruction: TupleInstruction, prop: property) -> ClauseInfo[TTable]:
|
233
|
+
temp_table: tp.Type[Table] = table
|
204
234
|
|
205
|
-
|
206
|
-
tree_list = TreeInstruction(function).to_list()
|
207
|
-
temp_table: tp.Type[Table] = self._table
|
208
|
-
|
209
|
-
table_list: list[Table] = tree_list[index].nested_element.parents[1:]
|
235
|
+
_, *table_list = tuple_instruction.nested_element.parents
|
210
236
|
counter: int = 0
|
211
237
|
while prop not in temp_table.__properties_mapped__:
|
212
238
|
new_table: TTable = getattr(temp_table(), table_list[counter])
|
213
239
|
|
214
240
|
if not isinstance(new_table, type) or not issubclass(new_table, Table):
|
215
241
|
raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
|
216
|
-
self.
|
242
|
+
self._add_fk_relationship(temp_table, new_table)
|
217
243
|
|
218
244
|
temp_table = new_table
|
219
245
|
counter += 1
|
@@ -221,41 +247,41 @@ class DecompositionQueryBase[T: tp.Type[Table]](IDecompositionQuery[T]):
|
|
221
247
|
if prop in new_table.__properties_mapped__:
|
222
248
|
return ClauseInfo[TTable](new_table, prop, self.alias_children_resolver)
|
223
249
|
|
224
|
-
raise ValueError(f"property '{prop}' does not exist in any inherit
|
250
|
+
raise ValueError(f"property '{prop}' does not exist in any inherit tables.")
|
225
251
|
|
226
|
-
def
|
252
|
+
def __add_clause[Tc: tp.Type[Table]](self, clause: ClauseInfo[Tc]) -> None:
|
227
253
|
self._all_clauses.append(clause)
|
228
254
|
self._clauses_group_by_tables[clause._table].append(clause)
|
229
|
-
|
230
255
|
return None
|
231
256
|
|
232
|
-
def
|
233
|
-
|
234
|
-
old_table: tp.Type[Table] = self._table
|
257
|
+
def __add_necessary_fk(self, tuple_instruction: TupleInstruction, tables: tp.Type[Table]) -> None:
|
258
|
+
old_table = self.table
|
235
259
|
|
236
|
-
|
260
|
+
table_inherit_list: list[Table] = tuple_instruction.nested_element.parents[1:]
|
237
261
|
counter: int = 0
|
238
|
-
while
|
239
|
-
new_table: tp.Type[Table] = getattr(old_table(),
|
262
|
+
while tables not in old_table.__dict__.values():
|
263
|
+
new_table: tp.Type[Table] = getattr(old_table(), table_inherit_list[counter])
|
240
264
|
|
241
265
|
if not issubclass(new_table, Table):
|
242
266
|
raise ValueError(f"new_table var must be '{Table.__class__}' type and is '{type(new_table)}'")
|
243
267
|
|
244
|
-
self.
|
268
|
+
self._add_fk_relationship(old_table, new_table)
|
245
269
|
|
246
|
-
if
|
247
|
-
self.
|
248
|
-
return None
|
270
|
+
if tables in new_table.__dict__.values():
|
271
|
+
return self._add_fk_relationship(new_table, tables)
|
249
272
|
|
250
273
|
old_table = new_table
|
251
274
|
counter += 1
|
252
275
|
|
253
|
-
self.
|
254
|
-
return None
|
276
|
+
return self._add_fk_relationship(old_table, tables)
|
255
277
|
|
256
278
|
@property
|
257
279
|
def table(self) -> T:
|
258
|
-
return self.
|
280
|
+
return self.tables[0] if isinstance(self.tables, tp.Iterable) else self.tables
|
281
|
+
|
282
|
+
@property
|
283
|
+
def tables(self) -> T:
|
284
|
+
return self._tables
|
259
285
|
|
260
286
|
@property
|
261
287
|
def lambda_query[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]:
|
@@ -271,11 +297,11 @@ class DecompositionQueryBase[T: tp.Type[Table]](IDecompositionQuery[T]):
|
|
271
297
|
|
272
298
|
@property
|
273
299
|
def has_foreign_keys(self) -> bool:
|
274
|
-
return len(self.
|
300
|
+
return len(self._joins) > 0
|
275
301
|
|
276
302
|
@property
|
277
303
|
def fk_relationship(self) -> set[tuple[tp.Type[Table], tp.Type[Table]]]:
|
278
|
-
return self.
|
304
|
+
return self._joins
|
279
305
|
|
280
306
|
@property
|
281
307
|
@abc.abstractmethod
|
@@ -299,25 +325,14 @@ class DecompositionQueryBase[T: tp.Type[Table]](IDecompositionQuery[T]):
|
|
299
325
|
self._alias_name = value
|
300
326
|
|
301
327
|
def stringify_foreign_key(self, sep: str = "\n") -> str:
|
302
|
-
|
303
|
-
for
|
304
|
-
graph[left].append(right)
|
305
|
-
|
306
|
-
dfs = DFSTraversal.sort(graph)[::-1]
|
307
|
-
query: list = []
|
308
|
-
for l_tbl in dfs:
|
309
|
-
list_r_tbl = graph[l_tbl]
|
310
|
-
if not list_r_tbl:
|
311
|
-
continue
|
312
|
-
|
313
|
-
for r_tbl in list_r_tbl:
|
314
|
-
lambda_relationship = ForeignKey.MAPPED[l_tbl.__table_name__].referenced_tables[r_tbl.__table_name__].relationship
|
328
|
+
sorted_joins = JoinSelector.sort_join_selectors(self._joins)
|
329
|
+
return f"{sep}".join([join.query for join in sorted_joins])
|
315
330
|
|
316
|
-
|
317
|
-
|
331
|
+
def _add_fk_relationship[T1: tp.Type[Table], T2: tp.Type[Table]](self, t1: T1, t2: T2) -> None:
|
332
|
+
lambda_relationship = ForeignKey.MAPPED[t1.__table_name__].referenced_tables[t2.__table_name__].relationship
|
318
333
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
self.
|
323
|
-
return
|
334
|
+
tables = list(self._tables)
|
335
|
+
if t2 not in tables:
|
336
|
+
tables.append(t2)
|
337
|
+
self._tables = tuple(tables)
|
338
|
+
return self._joins.add(JoinSelector[T1, T2](t1, t2, self._by, where=lambda_relationship))
|
@@ -12,11 +12,15 @@ if tp.TYPE_CHECKING:
|
|
12
12
|
from .IQueryCommand import IQuery
|
13
13
|
|
14
14
|
|
15
|
-
class IDecompositionQuery[T: tp.Type[Table]](IQuery):
|
15
|
+
class IDecompositionQuery[T: tp.Type[Table], *Ts](IQuery):
|
16
16
|
@property
|
17
17
|
@abc.abstractmethod
|
18
18
|
def table(self) -> T: ...
|
19
19
|
|
20
|
+
@property
|
21
|
+
@abc.abstractmethod
|
22
|
+
def tables(self) -> tuple[*Ts]: ...
|
23
|
+
|
20
24
|
@property
|
21
25
|
@abc.abstractmethod
|
22
26
|
def lambda_query[*Ts](self) -> tp.Callable[[T], tuple[*Ts]]: ...
|
@@ -9,7 +9,7 @@ class IRepositoryBase[T](ABC):
|
|
9
9
|
return f"{IRepositoryBase.__name__}: {self.__class__.__name__}"
|
10
10
|
|
11
11
|
@abstractmethod
|
12
|
-
def read_sql[TFlavour](self, query: str, flavour: Optional[Type[TFlavour]], **kwargs) -> tuple[TFlavour]: ...
|
12
|
+
def read_sql[TFlavour](self, cnx: T, query: str, flavour: Optional[Type[TFlavour]], **kwargs) -> tuple[TFlavour]: ...
|
13
13
|
|
14
14
|
@abstractmethod
|
15
15
|
def executemany_with_values(self, query: str, values) -> None: ...
|