ormlambda 3.35.3__py3-none-any.whl → 4.0.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/__init__.py +79 -51
- ormlambda/caster/caster.py +6 -1
- ormlambda/common/abstract_classes/__init__.py +0 -2
- ormlambda/common/enums/__init__.py +1 -0
- ormlambda/common/enums/order_type.py +9 -0
- ormlambda/common/errors/__init__.py +13 -3
- ormlambda/common/global_checker.py +86 -8
- ormlambda/common/interfaces/IQueryCommand.py +2 -2
- ormlambda/common/interfaces/__init__.py +0 -2
- ormlambda/dialects/__init__.py +75 -3
- ormlambda/dialects/default/base.py +1 -1
- ormlambda/dialects/mysql/__init__.py +35 -78
- ormlambda/dialects/mysql/base.py +226 -40
- ormlambda/dialects/mysql/clauses/ST_AsText.py +26 -0
- ormlambda/dialects/mysql/clauses/ST_Contains.py +30 -0
- ormlambda/dialects/mysql/clauses/__init__.py +1 -0
- ormlambda/dialects/mysql/repository/__init__.py +1 -0
- ormlambda/{databases/my_sql → dialects/mysql/repository}/repository.py +0 -5
- ormlambda/dialects/mysql/types.py +6 -0
- ormlambda/engine/base.py +26 -4
- ormlambda/errors.py +9 -0
- ormlambda/model/base_model.py +3 -10
- ormlambda/repository/base_repository.py +1 -1
- ormlambda/repository/interfaces/IRepositoryBase.py +0 -7
- ormlambda/repository/response.py +21 -8
- ormlambda/sql/__init__.py +12 -3
- ormlambda/sql/clause_info/__init__.py +0 -2
- ormlambda/sql/clause_info/clause_info.py +94 -76
- ormlambda/sql/clause_info/interface/IAggregate.py +14 -4
- ormlambda/sql/clause_info/interface/IClauseInfo.py +6 -11
- ormlambda/sql/clauses/alias.py +6 -37
- ormlambda/sql/clauses/count.py +21 -36
- ormlambda/sql/clauses/group_by.py +13 -19
- ormlambda/sql/clauses/having.py +2 -6
- ormlambda/sql/clauses/insert.py +3 -3
- ormlambda/sql/clauses/interfaces/__init__.py +0 -1
- ormlambda/sql/clauses/join/join_context.py +5 -12
- ormlambda/sql/clauses/joins.py +34 -52
- ormlambda/sql/clauses/limit.py +1 -2
- ormlambda/sql/clauses/offset.py +1 -2
- ormlambda/sql/clauses/order.py +17 -21
- ormlambda/sql/clauses/select.py +56 -28
- ormlambda/sql/clauses/update.py +13 -10
- ormlambda/sql/clauses/where.py +20 -39
- ormlambda/sql/column/__init__.py +1 -0
- ormlambda/sql/column/column.py +19 -12
- ormlambda/sql/column/column_proxy.py +117 -0
- ormlambda/sql/column_table_proxy.py +23 -0
- ormlambda/sql/comparer.py +31 -65
- ormlambda/sql/compiler.py +248 -58
- ormlambda/sql/context/__init__.py +304 -0
- ormlambda/sql/ddl.py +19 -5
- ormlambda/sql/elements.py +3 -0
- ormlambda/sql/foreign_key.py +42 -64
- ormlambda/sql/functions/__init__.py +0 -1
- ormlambda/sql/functions/concat.py +35 -38
- ormlambda/sql/functions/max.py +12 -36
- ormlambda/sql/functions/min.py +13 -28
- ormlambda/sql/functions/sum.py +17 -33
- ormlambda/sql/sqltypes.py +2 -0
- ormlambda/sql/table/__init__.py +1 -0
- ormlambda/sql/table/table.py +31 -45
- ormlambda/sql/table/table_proxy.py +88 -0
- ormlambda/sql/type_api.py +4 -1
- ormlambda/sql/types.py +15 -12
- ormlambda/statements/__init__.py +0 -2
- ormlambda/statements/base_statement.py +53 -91
- ormlambda/statements/interfaces/IStatements.py +77 -123
- ormlambda/statements/interfaces/__init__.py +1 -1
- ormlambda/statements/query_builder.py +296 -128
- ormlambda/statements/statements.py +122 -115
- ormlambda/statements/types.py +5 -25
- ormlambda/util/__init__.py +7 -100
- ormlambda/util/langhelpers.py +102 -0
- ormlambda/util/module_tree/dynamic_module.py +1 -1
- ormlambda/util/preloaded.py +80 -0
- ormlambda/util/typing.py +12 -3
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/METADATA +56 -79
- ormlambda-4.0.4.dist-info/RECORD +139 -0
- ormlambda/common/abstract_classes/clause_info_converter.py +0 -65
- ormlambda/common/abstract_classes/decomposition_query.py +0 -141
- ormlambda/common/abstract_classes/query_base.py +0 -15
- ormlambda/common/interfaces/ICustomAlias.py +0 -7
- ormlambda/common/interfaces/IDecompositionQuery.py +0 -33
- ormlambda/databases/__init__.py +0 -4
- ormlambda/databases/my_sql/__init__.py +0 -3
- ormlambda/databases/my_sql/clauses/ST_AsText.py +0 -37
- ormlambda/databases/my_sql/clauses/ST_Contains.py +0 -36
- ormlambda/databases/my_sql/clauses/__init__.py +0 -14
- ormlambda/databases/my_sql/clauses/count.py +0 -33
- ormlambda/databases/my_sql/clauses/delete.py +0 -9
- ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
- ormlambda/databases/my_sql/clauses/group_by.py +0 -17
- ormlambda/databases/my_sql/clauses/having.py +0 -12
- ormlambda/databases/my_sql/clauses/insert.py +0 -9
- ormlambda/databases/my_sql/clauses/joins.py +0 -14
- ormlambda/databases/my_sql/clauses/limit.py +0 -6
- ormlambda/databases/my_sql/clauses/offset.py +0 -6
- ormlambda/databases/my_sql/clauses/order.py +0 -8
- ormlambda/databases/my_sql/clauses/update.py +0 -8
- ormlambda/databases/my_sql/clauses/upsert.py +0 -9
- ormlambda/databases/my_sql/clauses/where.py +0 -7
- ormlambda/dialects/interface/__init__.py +0 -1
- ormlambda/dialects/interface/dialect.py +0 -78
- ormlambda/sql/clause_info/aggregate_function_base.py +0 -96
- ormlambda/sql/clause_info/clause_info_context.py +0 -87
- ormlambda/sql/clauses/interfaces/ISelect.py +0 -17
- ormlambda/sql/clauses/new_join.py +0 -119
- ormlambda/util/load_module.py +0 -21
- ormlambda/util/plugin_loader.py +0 -32
- ormlambda-3.35.3.dist-info/RECORD +0 -159
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/caster.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/boolean.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/date.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/decimal.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/json.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +0 -0
- /ormlambda/{databases/my_sql → dialects/mysql/repository}/pool_types.py +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/AUTHORS +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/LICENSE +0 -0
- {ormlambda-3.35.3.dist-info → ormlambda-4.0.4.dist-info}/WHEEL +0 -0
@@ -1,8 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
|
3
3
|
|
4
|
-
from ormlambda import
|
5
|
-
|
4
|
+
from ormlambda.sql.types import ASTERISK
|
6
5
|
|
7
6
|
if TYPE_CHECKING:
|
8
7
|
from ormlambda.engine.base import Engine
|
@@ -11,16 +10,14 @@ if TYPE_CHECKING:
|
|
11
10
|
from ormlambda.statements.types import OrderTypes
|
12
11
|
from ormlambda.sql.types import ColumnType
|
13
12
|
from ormlambda.statements.types import SelectCols
|
14
|
-
from ormlambda.statements.interfaces import IStatements_two_generic
|
15
13
|
from ormlambda.statements.types import TypeExists
|
16
|
-
from ormlambda.sql.clause_info import IAggregate
|
17
14
|
from ormlambda.statements.types import WhereTypes
|
15
|
+
from ormlambda.dialects import Dialect
|
18
16
|
|
17
|
+
from ormlambda.statements.interfaces import IStatements
|
18
|
+
from ormlambda.statements.base_statement import ClusterResponse
|
19
19
|
|
20
|
-
from ormlambda
|
21
|
-
from ormlambda.statements import BaseStatement
|
22
|
-
|
23
|
-
from ormlambda import Table, Column
|
20
|
+
from ormlambda import OrderType, Table
|
24
21
|
from ormlambda.common.enums import JoinType
|
25
22
|
from ormlambda.sql.clauses.join import JoinContext, TupleJoinType
|
26
23
|
|
@@ -40,23 +37,53 @@ def clear_list[T, **P](f: Callable[Concatenate[Statements, P], T]) -> Callable[P
|
|
40
37
|
except Exception as err:
|
41
38
|
raise err
|
42
39
|
finally:
|
43
|
-
ForeignKey.stored_calls.clear()
|
44
40
|
self._query_builder.clear()
|
45
41
|
|
46
42
|
return wrapper
|
47
43
|
|
48
44
|
|
49
|
-
class Statements[T: Table
|
45
|
+
class Statements[T: Table](IStatements[T]):
|
50
46
|
def __init__(self, model: T, engine: Engine) -> None:
|
51
|
-
|
52
|
-
self.
|
47
|
+
self._query_builder = QueryBuilder()
|
48
|
+
self._engine = engine
|
49
|
+
self._dialect = engine.dialect
|
50
|
+
self._query: Optional[str] = None
|
51
|
+
self._model: T = model[0] if isinstance(model, Iterable) else model
|
52
|
+
|
53
|
+
if not issubclass(self._model, Table):
|
54
|
+
# Deben heredar de Table ya que es la forma que tenemos para identificar si estamos pasando una instancia del tipo que corresponde o no cuando llamamos a insert o upsert.
|
55
|
+
# Si no heredase de Table no sabriamos identificar el tipo de dato del que se trata porque al llamar a isinstance, obtendriamos el nombre de la clase que mapea a la tabla, Encargo, Edificio, Presupuesto y no podriamos crear una clase generica
|
56
|
+
raise Exception(f"'{model}' class does not inherit from Table class")
|
57
|
+
|
58
|
+
@property
|
59
|
+
def dialect(self) -> Dialect:
|
60
|
+
return self._dialect
|
61
|
+
|
62
|
+
@property
|
63
|
+
def engine(self) -> Engine:
|
64
|
+
return self._engine
|
65
|
+
|
66
|
+
@override
|
67
|
+
def table_exists(self) -> bool:
|
68
|
+
return self.engine.repository.table_exists(self._model.__table_name__)
|
69
|
+
|
70
|
+
def __repr__(self):
|
71
|
+
return f"<Model: {self.__class__.__name__}>"
|
72
|
+
|
73
|
+
@property
|
74
|
+
def query(self) -> str:
|
75
|
+
return self._query
|
76
|
+
|
77
|
+
@property
|
78
|
+
def model(self) -> Type[T]:
|
79
|
+
return self._model
|
53
80
|
|
54
81
|
@override
|
55
82
|
def create_table(self, if_exists: TypeExists = "fail") -> None:
|
56
83
|
name: str = self._model.__table_name__
|
57
|
-
if self.
|
84
|
+
if self.engine.repository.table_exists(name):
|
58
85
|
if if_exists == "replace":
|
59
|
-
self.
|
86
|
+
self.drop_table()
|
60
87
|
|
61
88
|
elif if_exists == "fail":
|
62
89
|
raise ValueError(f"Table '{self._model.__table_name__}' already exists")
|
@@ -64,7 +91,7 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
64
91
|
elif if_exists == "append":
|
65
92
|
counter: int = 0
|
66
93
|
char: str = ""
|
67
|
-
while self.
|
94
|
+
while self.engine.repository.table_exists(name + char):
|
68
95
|
counter += 1
|
69
96
|
char = f"_{counter}"
|
70
97
|
name += char
|
@@ -74,18 +101,25 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
74
101
|
return new_model.create_table(self.dialect)
|
75
102
|
|
76
103
|
query = self.model.create_table(self.dialect)
|
77
|
-
self.
|
104
|
+
self.engine.repository.execute(query)
|
105
|
+
return None
|
106
|
+
|
107
|
+
@override
|
108
|
+
def drop_table(self) -> None:
|
109
|
+
q = self.model.drop_table(self.dialect)
|
110
|
+
self.engine.repository.execute(q)
|
78
111
|
return None
|
79
112
|
|
80
113
|
@override
|
81
114
|
@clear_list
|
82
115
|
def insert(self, instances: T | list[T]) -> None:
|
83
|
-
insert = clauses.Insert(self._model, self.repository, self._dialect)
|
116
|
+
insert = clauses.Insert(self._model, self.engine.repository, self._dialect)
|
84
117
|
insert.insert(instances)
|
85
118
|
insert.execute()
|
86
119
|
return None
|
87
120
|
|
88
121
|
@override
|
122
|
+
@clear_list
|
89
123
|
def delete(self, instances: Optional[T | list[T]] = None) -> None:
|
90
124
|
if instances is None:
|
91
125
|
response = self.select()
|
@@ -95,7 +129,7 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
95
129
|
# We always going to have a tuple of one element
|
96
130
|
return self.delete(response)
|
97
131
|
|
98
|
-
delete = clauses.Delete(self._model, self.
|
132
|
+
delete = clauses.Delete(self._model, self.engine.repository, engine=self._engine)
|
99
133
|
delete.delete(instances)
|
100
134
|
delete.execute()
|
101
135
|
# not necessary to call self._query_builder.clear() because select() method already call it
|
@@ -104,7 +138,7 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
104
138
|
@override
|
105
139
|
@clear_list
|
106
140
|
def upsert(self, instances: T | list[T]) -> None:
|
107
|
-
upsert = clauses.Upsert(self._model, self.
|
141
|
+
upsert = clauses.Upsert(self._model, self.engine.repository, engine=self._engine)
|
108
142
|
upsert.upsert(instances)
|
109
143
|
upsert.execute()
|
110
144
|
return None
|
@@ -112,107 +146,96 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
112
146
|
@override
|
113
147
|
@clear_list
|
114
148
|
def update(self, dicc: dict[str, Any] | list[dict[str, Any]]) -> None:
|
115
|
-
update = clauses.Update(self._model, self.
|
149
|
+
update = clauses.Update(self._model, self.engine.repository, self._query_builder.components.where, engine=self._engine)
|
116
150
|
update.update(dicc)
|
117
151
|
update.execute()
|
118
152
|
|
119
153
|
return None
|
120
154
|
|
121
155
|
@override
|
122
|
-
def limit(self, number: int) ->
|
123
|
-
limit = clauses.Limit(number, dialect=self._dialect)
|
156
|
+
def limit(self, number: int) -> IStatements[T]:
|
124
157
|
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
158
|
+
limit = clauses.Limit(number=number)
|
125
159
|
self._query_builder.add_statement(limit)
|
126
160
|
return self
|
127
161
|
|
128
162
|
@override
|
129
|
-
def offset(self, number: int) ->
|
130
|
-
offset = clauses.Offset(number
|
163
|
+
def offset(self, number: int) -> IStatements[T]:
|
164
|
+
offset = clauses.Offset(number=number)
|
131
165
|
self._query_builder.add_statement(offset)
|
132
166
|
return self
|
133
167
|
|
134
168
|
@override
|
135
169
|
def count[TProp](
|
136
170
|
self,
|
137
|
-
selection:
|
138
|
-
alias="count",
|
139
|
-
execute: bool = False,
|
171
|
+
selection: Optional[SelectCols[T, TProp] | str] = ASTERISK,
|
172
|
+
alias: AliasType = "count",
|
140
173
|
) -> Optional[int]:
|
141
|
-
if
|
142
|
-
return self.select_one(
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
alias_clause=alias,
|
148
|
-
context=self._query_builder._context,
|
149
|
-
dialect=self._dialect,
|
150
|
-
)
|
174
|
+
if selection == ASTERISK:
|
175
|
+
return self.select_one(lambda x: clauses.Count(x, alias), flavour=dict)[alias]
|
176
|
+
|
177
|
+
# get first position because 'resolved_callback_object' return an, alway Iterable and we should only pass one column
|
178
|
+
res = GlobalChecker.resolved_callback_object(self.model, selection)[0]
|
179
|
+
return self.select_one(lambda x: clauses.Count(res, alias), flavour=dict)[alias]
|
151
180
|
|
152
181
|
@override
|
153
|
-
def where(self, conditions: WhereTypes) ->
|
182
|
+
def where(self, conditions: WhereTypes[T], restrictive: bool = True) -> IStatements[T]:
|
154
183
|
# FIXME [x]: I've wrapped self._model into tuple to pass it instance attr. Idk if it's correct
|
184
|
+
result = GlobalChecker.resolved_callback_object(self.model, conditions)
|
155
185
|
|
156
|
-
|
157
|
-
|
158
|
-
conditions = (conditions,)
|
159
|
-
self._query_builder.add_statement(clauses.Where(*conditions))
|
186
|
+
where = clauses.Where(*result, restrictive=restrictive)
|
187
|
+
self._query_builder.add_statement(where)
|
160
188
|
return self
|
161
189
|
|
162
190
|
@override
|
163
|
-
def having(self, conditions:
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
self._query_builder.add_statement(clauses.Having(*conditions))
|
191
|
+
def having(self, conditions: ColumnType, restrictive: bool = True) -> IStatements[T]:
|
192
|
+
result = GlobalChecker.resolved_callback_object(self.model, conditions)
|
193
|
+
having = clauses.Having(*result, restrictive=restrictive)
|
194
|
+
self._query_builder.add_statement(having)
|
168
195
|
return self
|
169
196
|
|
170
197
|
@override
|
171
|
-
def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) ->
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
198
|
+
def order[TValue](self, columns: str | Callable[[T], TValue], order_type: OrderTypes = OrderType.ASC) -> IStatements[T]:
|
199
|
+
if isinstance(columns, str):
|
200
|
+
callable_func = lambda x: columns # noqa: E731
|
201
|
+
else:
|
202
|
+
callable_func = columns
|
176
203
|
|
177
|
-
|
178
|
-
|
179
|
-
|
204
|
+
res = GlobalChecker.resolved_callback_object(self.model, callable_func)
|
205
|
+
deferred_op = clauses.Order(*res, order_type=order_type)
|
206
|
+
self._query_builder.add_statement(deferred_op)
|
207
|
+
|
208
|
+
return self
|
180
209
|
|
181
210
|
@override
|
182
211
|
def max[TProp](
|
183
212
|
self,
|
184
213
|
column: SelectCols[T, TProp],
|
185
|
-
alias:
|
186
|
-
execute: bool = False,
|
214
|
+
alias: AliasType = "max",
|
187
215
|
) -> int:
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
216
|
+
res = GlobalChecker.resolved_callback_object(self.model, column)[0]
|
217
|
+
|
218
|
+
return self.select_one(lambda x: func.Max(res, alias), flavour=dict)[alias]
|
192
219
|
|
193
220
|
@override
|
194
221
|
def min[TProp](
|
195
222
|
self,
|
196
223
|
column: SelectCols[T, TProp],
|
197
|
-
alias:
|
198
|
-
execute: bool = False,
|
224
|
+
alias: AliasType = "min",
|
199
225
|
) -> int:
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
226
|
+
res = GlobalChecker.resolved_callback_object(self.model, column)[0]
|
227
|
+
|
228
|
+
return self.select_one(lambda x: func.Min(res, alias), flavour=dict)[alias]
|
204
229
|
|
205
230
|
@override
|
206
231
|
def sum[TProp](
|
207
232
|
self,
|
208
233
|
column: SelectCols[T, TProp],
|
209
|
-
alias:
|
210
|
-
execute: bool = False,
|
234
|
+
alias: AliasType = "sum",
|
211
235
|
) -> int:
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context, dialect=self._dialect)
|
236
|
+
res = GlobalChecker.resolved_callback_object(self.model, column)[0]
|
237
|
+
|
238
|
+
return self.select_one(lambda x: func.Sum(res, alias), flavour=dict)[alias]
|
216
239
|
|
217
240
|
@override
|
218
241
|
def join[LTable: Table, LProp, RTable: Table, RProp](self, joins: tuple[TupleJoinType[LTable, LProp, RTable, RProp]]) -> JoinContext[tuple[*TupleJoinType[LTable, LProp, RTable, RProp]]]:
|
@@ -226,39 +249,35 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
226
249
|
*,
|
227
250
|
flavour: Optional[Type[TFlavour]] = None,
|
228
251
|
by: JoinType = JoinType.INNER_JOIN,
|
252
|
+
alias: Optional[AliasType[T]] = None,
|
253
|
+
avoid_duplicates: bool = False,
|
229
254
|
**kwargs,
|
230
255
|
):
|
231
|
-
if "alias" in kwargs:
|
232
|
-
alias = kwargs.pop("alias")
|
233
|
-
kwargs["alias_clause"] = alias
|
234
|
-
select_clause = GlobalChecker.resolved_callback_object(selector, self._models)
|
235
|
-
|
236
256
|
if selector is None:
|
237
257
|
# COMMENT: if we do not specify any lambda function we assumed the user want to retreive only elements of the Model itself avoiding other models
|
238
|
-
result = self.select(
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
258
|
+
result = self.select(
|
259
|
+
selector=lambda x: x,
|
260
|
+
flavour=flavour,
|
261
|
+
by=by,
|
262
|
+
avoid_duplicates=avoid_duplicates,
|
263
|
+
**kwargs,
|
264
|
+
)
|
265
|
+
return result
|
266
|
+
select_clause = GlobalChecker.resolved_callback_object(self.model, selector)
|
244
267
|
|
245
268
|
select = clauses.Select(
|
246
|
-
self.
|
269
|
+
table=self.model,
|
247
270
|
columns=select_clause,
|
248
|
-
|
249
|
-
|
271
|
+
alias=alias,
|
272
|
+
avoid_duplicates=avoid_duplicates,
|
250
273
|
)
|
274
|
+
|
251
275
|
self._query_builder.add_statement(select)
|
252
276
|
|
253
277
|
self._query_builder.by = by
|
254
278
|
self._query: str = self._query_builder.query(self._dialect)
|
255
279
|
|
256
|
-
|
257
|
-
result = self._return_flavour(self.query, flavour, select, **kwargs)
|
258
|
-
if issubclass(flavour, tuple) and isinstance(select_clause, Column | ClauseInfo):
|
259
|
-
return tuple([x[0] for x in result])
|
260
|
-
return result
|
261
|
-
return self._return_model(select, self.query)
|
280
|
+
return ClusterResponse(select, self._engine, flavour, self._query).cluster_data()
|
262
281
|
|
263
282
|
@override
|
264
283
|
def select_one[TValue, TFlavour, *Ts](
|
@@ -282,11 +301,8 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
282
301
|
# select columns from different tables using a join query
|
283
302
|
# FIXME [x]: before it was if len(response) == 1 and len(response[0]) == 1: return response[0][0]
|
284
303
|
if len(response) == 1:
|
285
|
-
|
286
|
-
|
287
|
-
else:
|
288
|
-
return response[0]
|
289
|
-
return tuple([res[0] for res in response])
|
304
|
+
return response[0]
|
305
|
+
return response
|
290
306
|
|
291
307
|
@override
|
292
308
|
def first[TValue, TFlavour, *Ts](
|
@@ -305,22 +321,13 @@ class Statements[T: Table, TRepo](BaseStatement[T, None]):
|
|
305
321
|
)
|
306
322
|
|
307
323
|
@override
|
308
|
-
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T], Any]) ->
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
# Only can be one LIMIT SQL parameter. We only use the last LimitQuery
|
313
|
-
self._query_builder.add_statement(groupby)
|
324
|
+
def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T], Any]) -> IStatements[T]:
|
325
|
+
result = GlobalChecker.resolved_callback_object(self.model, column)
|
326
|
+
deferred_op = clauses.GroupBy(*result)
|
327
|
+
self._query_builder.add_statement(deferred_op)
|
314
328
|
return self
|
315
329
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
return clauses.Alias(
|
321
|
-
table=column.table,
|
322
|
-
column=column,
|
323
|
-
alias_clause=alias,
|
324
|
-
context=self._query_builder._context,
|
325
|
-
dialect=self.dialect,
|
326
|
-
)
|
330
|
+
def compile(self) -> str:
|
331
|
+
if not self._query:
|
332
|
+
return self._query_builder.query(self._dialect)
|
333
|
+
return self._query
|
ormlambda/statements/types.py
CHANGED
@@ -7,50 +7,30 @@ from typing import (
|
|
7
7
|
Union,
|
8
8
|
TYPE_CHECKING,
|
9
9
|
)
|
10
|
-
import enum
|
11
10
|
|
12
11
|
|
13
12
|
if TYPE_CHECKING:
|
14
13
|
from ormlambda.common.enums import JoinType
|
15
14
|
from ormlambda.sql.comparer import Comparer
|
16
15
|
from ormlambda.sql.types import ColumnType
|
16
|
+
from ormlambda.common.enums import OrderType
|
17
17
|
|
18
18
|
type OrderTypes = Literal["ASC", "DESC"] | OrderType | Iterable[OrderType]
|
19
19
|
|
20
20
|
|
21
|
-
|
22
|
-
def __str__(self):
|
23
|
-
return super().__str__()
|
24
|
-
|
25
|
-
ASC = "ASC"
|
26
|
-
DESC = "DESC"
|
27
|
-
|
28
|
-
|
29
|
-
type Tuple[T] = tuple[T, ...]
|
30
|
-
|
31
|
-
type Select2[T1, T2] = tuple[Tuple[T1], Tuple[T2]]
|
32
|
-
type Select3[T1, T2, T3] = tuple[*Select2[T1, T2], Tuple[T3]]
|
33
|
-
type Select4[T1, T2, T3, T4] = tuple[*Select3[T1, T2, T3], Tuple[T4]]
|
34
|
-
type Select5[T1, T2, T3, T4, T5] = tuple[*Select4[T1, T2, T3, T4], Tuple[T5]]
|
35
|
-
type Select6[T1, T2, T3, T4, T5, T6] = tuple[*Select5[T1, T2, T3, T4, T5], Tuple[T6]]
|
36
|
-
type Select7[T1, T2, T3, T4, T5, T6, T7] = tuple[*Select6[T1, T2, T3, T4, T5, T6], Tuple[T7]]
|
37
|
-
type Select8[T1, T2, T3, T4, T5, T6, T7, T8] = tuple[*Select7[T1, T2, T3, T4, T5, T6, T7], Tuple[T8]]
|
38
|
-
type Select9[T1, T2, T3, T4, T5, T6, T7, T8, T9] = tuple[*Select8[T1, T2, T3, T4, T5, T6, T7, T8], Tuple[T9]]
|
39
|
-
type Select10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] = tuple[*Select9[T1, T2, T3, T4, T5, T6, T7, T8, T9], Tuple[T10]]
|
40
|
-
|
21
|
+
type Tuple[*T] = tuple[tuple[*T], ...]
|
41
22
|
type WhereCondition[T, T1] = Callable[[T, T1], bool]
|
42
23
|
type JoinCondition[T, T1] = tuple[T1, WhereCondition[T, T1], Optional[JoinType]]
|
43
24
|
|
44
25
|
|
45
|
-
# TODOH [x]: This var is duplicated from 'src\ormlambda\databases\my_sql\clauses\create_database.py'
|
46
26
|
type TypeExists = Literal["fail", "replace", "append"]
|
47
27
|
|
48
28
|
|
49
|
-
type WhereTypes[LTable
|
29
|
+
type WhereTypes[LTable] = Union[
|
50
30
|
bool,
|
51
31
|
Comparer,
|
52
|
-
|
53
|
-
|
32
|
+
Callable[[LTable], WhereTypes[LTable]],
|
33
|
+
Iterable[WhereTypes[LTable]],
|
54
34
|
]
|
55
35
|
|
56
36
|
|
ormlambda/util/__init__.py
CHANGED
@@ -1,102 +1,9 @@
|
|
1
|
-
from .
|
2
|
-
from .
|
3
|
-
import
|
4
|
-
import inspect
|
5
|
-
from typing import Any, Literal, Optional, overload, get_origin, TypeGuard, TypeAliasType
|
6
|
-
from ormlambda.util.typing import LITERAL_TYPES, _AnnotationScanType
|
7
|
-
from .plugin_loader import PluginLoader # noqa: F401
|
1
|
+
from .preloaded import preload_module as preload_module
|
2
|
+
from .preloaded import import_prefix as import_prefix
|
3
|
+
from . import preloaded as preloaded
|
8
4
|
|
5
|
+
from .langhelpers import get_cls_kwargs as get_cls_kwargs
|
6
|
+
from .langhelpers import PluginLoader as PluginLoader
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
co_varkeywords = inspect.CO_VARKEYWORDS
|
13
|
-
except AttributeError:
|
14
|
-
# https://docs.python.org/3/library/inspect.html
|
15
|
-
# The flags are specific to CPython, and may not be defined in other
|
16
|
-
# Python implementations. Furthermore, the flags are an implementation
|
17
|
-
# detail, and can be removed or deprecated in future Python releases.
|
18
|
-
spec = inspect.getfullargspec(fn)
|
19
|
-
return spec[0], bool(spec[2])
|
20
|
-
else:
|
21
|
-
# use fn.__code__ plus flags to reduce method call overhead
|
22
|
-
co = fn.__code__
|
23
|
-
nargs = co.co_argcount
|
24
|
-
return (
|
25
|
-
list(co.co_varnames[:nargs]),
|
26
|
-
bool(co.co_flags & co_varkeywords),
|
27
|
-
)
|
28
|
-
|
29
|
-
|
30
|
-
@overload
|
31
|
-
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[True] = ...) -> set[str]: ...
|
32
|
-
|
33
|
-
|
34
|
-
@overload
|
35
|
-
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[False] = ...) -> Optional[set[str]]: ...
|
36
|
-
|
37
|
-
|
38
|
-
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: bool = False) -> Optional[set[str]]:
|
39
|
-
"""
|
40
|
-
Get the keyword arguments for a class constructor.
|
41
|
-
Args:
|
42
|
-
cls: The class to inspect.
|
43
|
-
_set: A set to store the keyword arguments.
|
44
|
-
raiseerr: Whether to raise an error if the class is not found.
|
45
|
-
Returns:
|
46
|
-
A set of keyword arguments for the class constructor.
|
47
|
-
"""
|
48
|
-
toplevel = _set is None
|
49
|
-
if toplevel:
|
50
|
-
_set = set()
|
51
|
-
assert _set is not None
|
52
|
-
|
53
|
-
ctr = cls.__dict__.get("__init__", False)
|
54
|
-
|
55
|
-
has_init = ctr and isinstance(ctr, types.FunctionType) and isinstance(ctr.__code__, types.CodeType)
|
56
|
-
|
57
|
-
if has_init:
|
58
|
-
names, has_kw = _inspect_func_args(ctr)
|
59
|
-
_set.update(names)
|
60
|
-
|
61
|
-
if not has_kw and not toplevel:
|
62
|
-
if raiseerr:
|
63
|
-
raise TypeError(f"given cls {cls} doesn't have an __init__ method")
|
64
|
-
else:
|
65
|
-
return None
|
66
|
-
else:
|
67
|
-
has_kw = False
|
68
|
-
|
69
|
-
if not has_init or has_kw:
|
70
|
-
for c in cls.__bases__:
|
71
|
-
if get_cls_kwargs(c, _set=_set) is None:
|
72
|
-
break
|
73
|
-
|
74
|
-
_set.discard("self")
|
75
|
-
return _set
|
76
|
-
|
77
|
-
|
78
|
-
def avoid_sql_injection(name: str):
|
79
|
-
if any(char in name for char in [";", "--", "/*", "*/"]):
|
80
|
-
raise ValueError("SQL injection detected")
|
81
|
-
|
82
|
-
|
83
|
-
def is_literal(type_: Any) -> bool:
|
84
|
-
return get_origin(type) in LITERAL_TYPES
|
85
|
-
|
86
|
-
|
87
|
-
def is_pep695(type_: _AnnotationScanType) -> TypeGuard[TypeAliasType]:
|
88
|
-
return isinstance(type_, TypeAliasType)
|
89
|
-
|
90
|
-
|
91
|
-
def make_hashable(item: Any) -> Any:
|
92
|
-
if isinstance(item, dict):
|
93
|
-
return tuple(sorted((k, make_hashable(x)) for k, x in item.items()))
|
94
|
-
|
95
|
-
if isinstance(item, (list | set)):
|
96
|
-
return tuple(make_hashable(x) for x in item)
|
97
|
-
if hasattr(item, "__iter__") and not isinstance(item, str | bytes):
|
98
|
-
try:
|
99
|
-
return tuple(make_hashable(x) for x in item)
|
100
|
-
except TypeError:
|
101
|
-
return item # if it fails, it's already hashable
|
102
|
-
return item
|
8
|
+
from .typing import is_literal as is_literal
|
9
|
+
from .typing import is_pep695 as is_pep695
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import types
|
2
|
+
import inspect
|
3
|
+
from typing import Literal, Optional, overload, Callable
|
4
|
+
from types import ModuleType
|
5
|
+
from ormlambda import errors
|
6
|
+
|
7
|
+
|
8
|
+
def _inspect_func_args(fn) -> tuple[list[str], bool]:
|
9
|
+
try:
|
10
|
+
co_varkeywords = inspect.CO_VARKEYWORDS
|
11
|
+
except AttributeError:
|
12
|
+
# https://docs.python.org/3/library/inspect.html
|
13
|
+
# The flags are specific to CPython, and may not be defined in other
|
14
|
+
# Python implementations. Furthermore, the flags are an implementation
|
15
|
+
# detail, and can be removed or deprecated in future Python releases.
|
16
|
+
spec = inspect.getfullargspec(fn)
|
17
|
+
return spec[0], bool(spec[2])
|
18
|
+
else:
|
19
|
+
# use fn.__code__ plus flags to reduce method call overhead
|
20
|
+
co = fn.__code__
|
21
|
+
nargs = co.co_argcount
|
22
|
+
return (
|
23
|
+
list(co.co_varnames[:nargs]),
|
24
|
+
bool(co.co_flags & co_varkeywords),
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
@overload
|
29
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[True] = ...) -> set[str]: ...
|
30
|
+
|
31
|
+
|
32
|
+
@overload
|
33
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: Literal[False] = ...) -> Optional[set[str]]: ...
|
34
|
+
|
35
|
+
|
36
|
+
def get_cls_kwargs(cls: type, *, _set: Optional[set[str]] = None, raiseerr: bool = False) -> Optional[set[str]]:
|
37
|
+
"""
|
38
|
+
Get the keyword arguments for a class constructor.
|
39
|
+
Args:
|
40
|
+
cls: The class to inspect.
|
41
|
+
_set: A set to store the keyword arguments.
|
42
|
+
raiseerr: Whether to raise an error if the class is not found.
|
43
|
+
Returns:
|
44
|
+
A set of keyword arguments for the class constructor.
|
45
|
+
"""
|
46
|
+
toplevel = _set is None
|
47
|
+
if toplevel:
|
48
|
+
_set = set()
|
49
|
+
assert _set is not None
|
50
|
+
|
51
|
+
ctr = cls.__dict__.get("__init__", False)
|
52
|
+
|
53
|
+
has_init = ctr and isinstance(ctr, types.FunctionType) and isinstance(ctr.__code__, types.CodeType)
|
54
|
+
|
55
|
+
if has_init:
|
56
|
+
names, has_kw = _inspect_func_args(ctr)
|
57
|
+
_set.update(names)
|
58
|
+
|
59
|
+
if not has_kw and not toplevel:
|
60
|
+
if raiseerr:
|
61
|
+
raise TypeError(f"given cls {cls} doesn't have an __init__ method")
|
62
|
+
else:
|
63
|
+
return None
|
64
|
+
else:
|
65
|
+
has_kw = False
|
66
|
+
|
67
|
+
if not has_init or has_kw:
|
68
|
+
for c in cls.__bases__:
|
69
|
+
if get_cls_kwargs(c, _set=_set) is None:
|
70
|
+
break
|
71
|
+
|
72
|
+
_set.discard("self")
|
73
|
+
return _set
|
74
|
+
|
75
|
+
|
76
|
+
class PluginLoader:
|
77
|
+
def __init__(self, group: str, auto_fn: Optional[Callable[..., ModuleType]] = None):
|
78
|
+
self.group = group
|
79
|
+
self.impls: dict[str, ModuleType] = {}
|
80
|
+
self.auto_fn = auto_fn
|
81
|
+
|
82
|
+
def clear(self):
|
83
|
+
self.impls.clear()
|
84
|
+
|
85
|
+
def load(self, name: str) -> Optional[ModuleType]:
|
86
|
+
if name in self.impls:
|
87
|
+
return self.impls[name]()
|
88
|
+
if self.auto_fn:
|
89
|
+
loader = self.auto_fn(name)
|
90
|
+
if loader:
|
91
|
+
self.impls[name] = loader
|
92
|
+
return loader()
|
93
|
+
raise errors.NoSuchModuleError(f"Can't load plugin: {self.group}:{name}")
|
94
|
+
|
95
|
+
def register(self, name: str, modulepath: str, objname: str) -> None:
|
96
|
+
def load():
|
97
|
+
mod = __import__(modulepath)
|
98
|
+
for token in modulepath.split(".")[1:]:
|
99
|
+
mod = getattr(mod, token)
|
100
|
+
return getattr(mod, objname)
|
101
|
+
|
102
|
+
self.impls[name] = load
|
@@ -236,7 +236,7 @@ class ModuleTree:
|
|
236
236
|
# we need to ensure that the object we going to add in table_list is the same
|
237
237
|
for name, obj in table_class:
|
238
238
|
if name == node.class_name:
|
239
|
-
table_list.append(obj.
|
239
|
+
table_list.append(obj.create_table())
|
240
240
|
return tuple(table_list)
|
241
241
|
|
242
242
|
@staticmethod
|