ormlambda 3.7.1__py3-none-any.whl → 3.11.0__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.
Files changed (40) hide show
  1. ormlambda/__init__.py +2 -0
  2. ormlambda/caster/base_caster.py +3 -3
  3. ormlambda/common/global_checker.py +1 -1
  4. ormlambda/components/select/ISelect.py +4 -4
  5. ormlambda/components/select/__init__.py +1 -1
  6. ormlambda/databases/my_sql/caster/caster.py +1 -0
  7. ormlambda/databases/my_sql/caster/types/bytes.py +3 -3
  8. ormlambda/databases/my_sql/caster/types/datetime.py +3 -3
  9. ormlambda/databases/my_sql/caster/types/float.py +3 -3
  10. ormlambda/databases/my_sql/caster/types/int.py +3 -3
  11. ormlambda/databases/my_sql/caster/types/iterable.py +3 -3
  12. ormlambda/databases/my_sql/caster/types/none.py +3 -3
  13. ormlambda/databases/my_sql/caster/types/string.py +3 -3
  14. ormlambda/databases/my_sql/clauses/__init__.py +1 -0
  15. ormlambda/databases/my_sql/clauses/alias.py +15 -21
  16. ormlambda/databases/my_sql/clauses/group_by.py +19 -20
  17. ormlambda/databases/my_sql/clauses/having.py +16 -0
  18. ormlambda/databases/my_sql/clauses/order.py +6 -1
  19. ormlambda/databases/my_sql/clauses/update.py +1 -1
  20. ormlambda/databases/my_sql/clauses/where.py +3 -3
  21. ormlambda/databases/my_sql/functions/concat.py +8 -6
  22. ormlambda/databases/my_sql/join_context.py +3 -3
  23. ormlambda/databases/my_sql/repository/repository.py +60 -13
  24. ormlambda/databases/my_sql/statements.py +73 -22
  25. ormlambda/databases/my_sql/types.py +73 -0
  26. ormlambda/engine/__init__.py +2 -0
  27. ormlambda/engine/create.py +35 -0
  28. ormlambda/engine/url.py +744 -0
  29. ormlambda/engine/utils.py +17 -0
  30. ormlambda/repository/base_repository.py +2 -3
  31. ormlambda/repository/interfaces/IRepositoryBase.py +1 -0
  32. ormlambda/sql/column.py +27 -2
  33. ormlambda/sql/foreign_key.py +36 -4
  34. ormlambda/sql/table/table_constructor.py +2 -2
  35. ormlambda/statements/interfaces/IStatements.py +37 -25
  36. ormlambda/statements/types.py +4 -1
  37. {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/METADATA +107 -8
  38. {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/RECORD +40 -35
  39. {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/LICENSE +0 -0
  40. {ormlambda-3.7.1.dist-info → ormlambda-3.11.0.dist-info}/WHEEL +0 -0
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Concatenate, Iterable, override, Type, TYPE_CHECKING, Any, Callable, Optional
2
+ from typing import Concatenate, Iterable, TypedDict, override, Type, TYPE_CHECKING, Any, Callable, Optional
3
3
  from mysql.connector import errors, errorcode
4
4
 
5
5
  from ormlambda.sql.clause_info.clause_info_context import ClauseInfoContext
@@ -28,11 +28,12 @@ from .clauses import InsertQuery
28
28
  from .clauses import Limit
29
29
  from .clauses import Offset
30
30
  from .clauses import Order
31
- from .clauses.select import Select
31
+ from .clauses import Select
32
32
 
33
33
  from .clauses import UpsertQuery
34
34
  from .clauses import UpdateQuery
35
35
  from .clauses import Where
36
+ from .clauses import Having
36
37
  from .clauses import Count
37
38
  from .clauses import GroupBy
38
39
  from .clauses import Alias
@@ -60,12 +61,23 @@ def clear_list[T, **P](f: Callable[Concatenate[MySQLStatements, P], T]) -> Calla
60
61
  return wrapper
61
62
 
62
63
 
64
+ class OrderType(TypedDict):
65
+ Select: Select
66
+ JoinSelector: JoinSelector
67
+ Where: Where
68
+ Order: Order
69
+ GroupBy: GroupBy
70
+ Having: Having
71
+ Limit: Limit
72
+ Offset: Offset
73
+
74
+
63
75
  class QueryBuilder(IQuery):
64
- __order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "Order", "GroupBy", "Limit", "Offset")
76
+ __order__: tuple[str, ...] = ("Select", "JoinSelector", "Where", "GroupBy", "Having", "Order", "Limit", "Offset")
65
77
 
66
78
  def __init__(self, by: JoinType = JoinType.INNER_JOIN):
67
79
  self._context = ClauseInfoContext()
68
- self._query_list: dict[str, IQuery] = {}
80
+ self._query_list: OrderType = {}
69
81
  self._by = by
70
82
 
71
83
  self._joins: Optional[IQuery] = None
@@ -113,6 +125,15 @@ class QueryBuilder(IQuery):
113
125
  def GROUP_BY(self) -> IQuery:
114
126
  return self._query_list.get("GroupBy", None)
115
127
 
128
+ @property
129
+ def HAVING(self) -> IQuery:
130
+ where = self._query_list.get("Having", None)
131
+ if not isinstance(where, Iterable):
132
+ if not where:
133
+ return ()
134
+ return (where,)
135
+ return where
136
+
116
137
  @property
117
138
  def LIMIT(self) -> IQuery:
118
139
  return self._query_list.get("Limit", None)
@@ -133,8 +154,9 @@ class QueryBuilder(IQuery):
133
154
  self.SELECT.query,
134
155
  JOINS,
135
156
  Where.join_condition(self.WHERE, True, self._context) if self.WHERE else None,
136
- self.ORDER.query if self.ORDER else None,
137
157
  self.GROUP_BY.query if self.GROUP_BY else None,
158
+ Having.join_condition(self.HAVING, True, self._context) if self.HAVING else None,
159
+ self.ORDER.query if self.ORDER else None,
138
160
  self.LIMIT.query if self.LIMIT else None,
139
161
  self.OFFSET.query if self.OFFSET else None,
140
162
  )
@@ -154,8 +176,8 @@ class QueryBuilder(IQuery):
154
176
  joins = set()
155
177
  # Always it's gonna be a set of two
156
178
  # FIXME [x]: Resolved when we get Compare object instead ClauseInfo. For instance, when we have multiples condition using '&' or '|'
157
- for _ in range(len(ForeignKey.stored_calls)):
158
- fk = ForeignKey.stored_calls.pop()
179
+ for fk in ForeignKey.stored_calls.copy():
180
+ fk = ForeignKey.stored_calls.pop(fk)
159
181
  self._context._add_table_alias(fk.tright, fk.alias)
160
182
  join = JoinSelector(fk.resolved_function(self._context), by, context=self._context, alias=fk.alias)
161
183
  joins.add(join)
@@ -278,7 +300,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
278
300
 
279
301
  if GlobalChecker.is_lambda_function(selection):
280
302
  selection = selection(*self.models)
281
- return Count[T](element=selection, alias_clause=alias_clause, context=self._query_builder._context)
303
+ return Count(element=selection, alias_clause=alias_clause, context=self._query_builder._context)
282
304
 
283
305
  @override
284
306
  def where(self, conditions: WhereTypes) -> IStatements_two_generic[T, MySQLConnection]:
@@ -291,6 +313,15 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
291
313
  self._query_builder.add_statement(Where(*conditions))
292
314
  return self
293
315
 
316
+ @override
317
+ def having(self, conditions: WhereTypes) -> IStatements_two_generic[T, MySQLConnection]:
318
+ if GlobalChecker.is_lambda_function(conditions):
319
+ conditions = GlobalChecker.resolved_callback_object(conditions, self._models)
320
+ if not isinstance(conditions, Iterable):
321
+ conditions = (conditions,)
322
+ self._query_builder.add_statement(Having(*conditions))
323
+ return self
324
+
294
325
  @override
295
326
  def order[TValue](self, columns: Callable[[T], TValue], order_type: OrderTypes) -> IStatements_two_generic[T, MySQLConnection]:
296
327
  query = GlobalChecker.resolved_callback_object(columns, self._models) if GlobalChecker.is_lambda_function(columns) else columns
@@ -299,23 +330,42 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
299
330
  return self
300
331
 
301
332
  @override
302
- def concat[*Ts](self, selector: Callable[[T], tuple[*Ts]], alias: bool = True, alias_name: str = "CONCAT") -> IAggregate:
303
- return func.Concat[T](self._model, selector, alias=alias, alias_name=alias_name, context=self._query_builder._context)
333
+ def concat(self, selector: str | Callable[[T], str], alias: str = "CONCAT") -> IAggregate:
334
+ return func.Concat[T](
335
+ values=selector,
336
+ alias_clause=alias,
337
+ context=self._query_builder._context,
338
+ )
304
339
 
305
340
  @override
306
- def max[TProp](self, column: Callable[[T], TProp], alias: str = "max", execute: bool = False) -> TProp:
341
+ def max[TProp](
342
+ self,
343
+ column: Callable[[T], TProp],
344
+ alias: str = "max",
345
+ execute: bool = False,
346
+ ) -> TProp:
307
347
  if execute is True:
308
348
  return self.select_one(self.max(column, alias, execute=False), flavour=dict)[alias]
309
349
  return func.Max(elements=column, alias_clause=alias, context=self._query_builder._context)
310
350
 
311
351
  @override
312
- def min[TProp](self, column: Callable[[T], TProp], alias: str = "min", execute: bool = False) -> TProp:
352
+ def min[TProp](
353
+ self,
354
+ column: Callable[[T], TProp],
355
+ alias: str = "min",
356
+ execute: bool = False,
357
+ ) -> TProp:
313
358
  if execute is True:
314
359
  return self.select_one(self.min(column, alias, execute=False), flavour=dict)[alias]
315
360
  return func.Min(elements=column, alias_clause=alias, context=self._query_builder._context)
316
361
 
317
362
  @override
318
- def sum[TProp](self, column: Callable[[T], TProp], alias: str = "sum", execute: bool = False) -> TProp:
363
+ def sum[TProp](
364
+ self,
365
+ column: Callable[[T], TProp],
366
+ alias: str = "sum",
367
+ execute: bool = False,
368
+ ) -> TProp:
319
369
  if execute is True:
320
370
  return self.select_one(self.sum(column, alias, execute=False), flavour=dict)[alias]
321
371
  return func.Sum(elements=column, alias_clause=alias, context=self._query_builder._context)
@@ -372,7 +422,7 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
372
422
  ):
373
423
  self.limit(1)
374
424
 
375
- response = self.select(selector=selector, flavour=flavour, by=by,**kwargs)
425
+ response = self.select(selector=selector, flavour=flavour, by=by, **kwargs)
376
426
 
377
427
  if not isinstance(response, Iterable):
378
428
  return response
@@ -406,16 +456,17 @@ class MySQLStatements[T: Table, *Ts](BaseStatement[T, MySQLConnection]):
406
456
  )
407
457
 
408
458
  @override
409
- def group_by(self, column: str | Callable[[T, *Ts], Any]):
410
- if isinstance(column, str):
411
- groupby = GroupBy[T, tuple[*Ts]](self._models, lambda x: column)
412
- else:
413
- groupby = GroupBy[T, tuple[*Ts]](self._models, column)
459
+ def groupby[TProp](self, column: ColumnType[TProp] | Callable[[T, *Ts], Any]):
460
+ groupby = GroupBy(column=column, context=self._query_builder._context)
414
461
  # Only can be one LIMIT SQL parameter. We only use the last LimitQuery
415
462
  self._query_builder.add_statement(groupby)
416
463
  return self
417
464
 
418
465
  @override
419
- def alias[TProp](self, column: ColumnType[TProp], alias: str) -> Alias[T, TProp]:
420
- ci = ClauseInfo(table=column.table, column=column, alias_clause=alias, context=self._query_builder._context)
421
- return Alias(ci, alias_clause=alias)
466
+ def alias[TProp](self, column: ColumnType[TProp], alias: str) -> ClauseInfo[T, TProp]:
467
+ return Alias(
468
+ table=column.table,
469
+ column=column,
470
+ alias_clause=alias,
471
+ context=self._query_builder._context,
472
+ )
@@ -0,0 +1,73 @@
1
+ from typing import Callable, Optional, Type, TypedDict, Any
2
+
3
+ from mysql.connector.constants import (
4
+ CharacterSet,
5
+ ClientFlag,
6
+ )
7
+ from mysql.connector.types import HandShakeType, BinaryProtocolType
8
+ from mysql.connector.conversion import MySQLConverter
9
+
10
+
11
+ class MySQLArgs(TypedDict):
12
+ client_flags: ClientFlag | int
13
+ sql_mode: Optional[str]
14
+ time_zone: Optional[str]
15
+ autocommit: bool
16
+ server_version: Optional[tuple[int, ...]]
17
+ handshake: Optional[HandShakeType]
18
+ conn_attrs: dict[str, str]
19
+
20
+ user: str
21
+ password: str
22
+ password1: str
23
+ password2: str
24
+ password3: str
25
+ database: str
26
+ host: str
27
+ port: int
28
+ unix_socket: Optional[str]
29
+ client_host: str
30
+ client_port: int
31
+ ssl: dict[str, Optional[str | bool | list[str]]]
32
+ ssl_disabled: bool
33
+ force_ipv6: bool
34
+ oci_config_file: Optional[str]
35
+ oci_config_profile: Optional[str]
36
+ webauthn_callback: Optional[str | Callable[[str], None]]
37
+ krb_service_principal: Optional[str]
38
+ openid_token_file: Optional[str]
39
+
40
+ use_unicode: bool
41
+ get_warnings: bool
42
+ raise_on_warnings: bool
43
+ connection_timeout: Optional[int]
44
+ read_timeout: Optional[int]
45
+ write_timeout: Optional[int]
46
+ buffered: bool
47
+ unread_result: bool
48
+ have_next_result: bool
49
+ raw: bool
50
+ in_transaction: bool
51
+ allow_local_infile: bool
52
+ allow_local_infile_in_path: Optional[str]
53
+
54
+ prepared_statements: Any
55
+ query_attrs: dict[str, BinaryProtocolType]
56
+
57
+ ssl_active: bool
58
+ auth_plugin: Optional[str]
59
+ auth_plugin_class: Optional[str]
60
+ pool_config_version: Any
61
+ converter_class: Optional[Type[MySQLConverter]]
62
+ converter_str_fallback: bool
63
+ compress: bool
64
+
65
+ consume_results: bool
66
+ init_command: Optional[str]
67
+ character_set: CharacterSet
68
+
69
+
70
+ __all__ = [
71
+ "MySQLArgs",
72
+ "ClientFlag",
73
+ ]
@@ -0,0 +1,2 @@
1
+ from .create import create_engine # noqa: F401
2
+ from .url import URL # noqa: F401
@@ -0,0 +1,35 @@
1
+ from typing import Any
2
+
3
+ from ormlambda.engine.url import URL, make_url
4
+ from ormlambda import BaseRepository
5
+
6
+
7
+ def create_engine(url: URL | str, **kwargs: Any) -> BaseRepository:
8
+ from ormlambda.databases import MySQLRepository
9
+
10
+ # create url.URL object
11
+ u = make_url(url)
12
+ url, kwargs = u._instantiate_plugins(kwargs)
13
+
14
+ repo_selector = {
15
+ "mysql": MySQLRepository,
16
+ }
17
+
18
+ if url.drivername not in repo_selector:
19
+ raise ValueError(f"drivername '{url.drivername}' not expected to load Repository class")
20
+
21
+ default_config = {
22
+ "user": url.username,
23
+ "password": url.password,
24
+ "host": url.host,
25
+ "database": url.database,
26
+ **kwargs,
27
+ }
28
+
29
+ if url.port:
30
+ try:
31
+ default_config["port"] = int(url.port)
32
+ except ValueError:
33
+ raise ValueError(f"The port must be an int. '{url.port}' passed.")
34
+
35
+ return repo_selector[url.drivername](**default_config)