ormlambda 1.5.0__py3-none-any.whl → 2.0.2__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 (54) hide show
  1. ormlambda/__init__.py +9 -2
  2. ormlambda/common/__init__.py +0 -1
  3. ormlambda/common/abstract_classes/__init__.py +1 -1
  4. ormlambda/common/abstract_classes/abstract_model.py +13 -228
  5. ormlambda/common/abstract_classes/non_query_base.py +7 -5
  6. ormlambda/common/abstract_classes/query_base.py +5 -2
  7. ormlambda/common/enums/__init__.py +1 -1
  8. ormlambda/common/enums/join_type.py +2 -1
  9. ormlambda/common/interfaces/IQueryCommand.py +2 -1
  10. ormlambda/common/interfaces/IRepositoryBase.py +4 -18
  11. ormlambda/common/interfaces/IStatements.py +33 -15
  12. ormlambda/common/interfaces/__init__.py +1 -1
  13. ormlambda/components/delete/abstract_delete.py +6 -3
  14. ormlambda/components/insert/IInsert.py +1 -1
  15. ormlambda/components/insert/abstract_insert.py +8 -4
  16. ormlambda/components/select/ISelect.py +1 -1
  17. ormlambda/components/select/table_column.py +5 -1
  18. ormlambda/components/update/IUpdate.py +1 -1
  19. ormlambda/components/update/__init__.py +1 -1
  20. ormlambda/components/update/abstract_update.py +9 -5
  21. ormlambda/components/upsert/__init__.py +1 -1
  22. ormlambda/components/upsert/abstract_upsert.py +8 -4
  23. ormlambda/components/where/abstract_where.py +6 -2
  24. ormlambda/databases/my_sql/clauses/__init__.py +1 -0
  25. ormlambda/databases/my_sql/clauses/count.py +35 -0
  26. ormlambda/databases/my_sql/clauses/create_database.py +17 -10
  27. ormlambda/databases/my_sql/clauses/delete.py +7 -4
  28. ormlambda/databases/my_sql/clauses/drop_database.py +1 -1
  29. ormlambda/databases/my_sql/clauses/drop_table.py +1 -1
  30. ormlambda/databases/my_sql/clauses/insert.py +4 -3
  31. ormlambda/databases/my_sql/clauses/joins.py +8 -7
  32. ormlambda/databases/my_sql/clauses/limit.py +1 -1
  33. ormlambda/databases/my_sql/clauses/offset.py +1 -1
  34. ormlambda/databases/my_sql/clauses/order.py +3 -3
  35. ormlambda/databases/my_sql/clauses/select.py +5 -5
  36. ormlambda/databases/my_sql/clauses/update.py +3 -3
  37. ormlambda/databases/my_sql/clauses/upsert.py +3 -3
  38. ormlambda/databases/my_sql/clauses/where_condition.py +5 -5
  39. ormlambda/databases/my_sql/repository.py +57 -27
  40. ormlambda/databases/my_sql/statements.py +200 -43
  41. ormlambda/model_base.py +2 -4
  42. ormlambda/utils/column.py +4 -3
  43. ormlambda/utils/dtypes.py +6 -8
  44. ormlambda/utils/foreign_key.py +55 -10
  45. ormlambda/utils/lambda_disassembler/__init__.py +1 -1
  46. ormlambda/utils/lambda_disassembler/dis_types.py +22 -25
  47. ormlambda/utils/lambda_disassembler/tree_instruction.py +1 -1
  48. ormlambda/utils/module_tree/dynamic_module.py +6 -4
  49. ormlambda/utils/table_constructor.py +39 -22
  50. {ormlambda-1.5.0.dist-info → ormlambda-2.0.2.dist-info}/METADATA +13 -9
  51. ormlambda-2.0.2.dist-info/RECORD +71 -0
  52. ormlambda-1.5.0.dist-info/RECORD +0 -70
  53. {ormlambda-1.5.0.dist-info → ormlambda-2.0.2.dist-info}/LICENSE +0 -0
  54. {ormlambda-1.5.0.dist-info → ormlambda-2.0.2.dist-info}/WHEEL +0 -0
@@ -1,9 +1,13 @@
1
+ from __future__ import annotations
1
2
  from abc import abstractmethod
2
- from .IUpsert import IUpsert
3
- from ...common.interfaces import IRepositoryBase
4
- from ...common.abstract_classes import NonQueryBase
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ormlambda import IRepositoryBase
7
+ from ormlambda import Table
5
8
 
6
- from ...utils import Table
9
+ from ormlambda.common.abstract_classes import NonQueryBase
10
+ from .IUpsert import IUpsert
7
11
 
8
12
 
9
13
  class UpsertQueryBase[T: Table, TRepo: IRepositoryBase](NonQueryBase[T, TRepo], IUpsert[T]):
@@ -1,7 +1,11 @@
1
+ from __future__ import annotations
1
2
  from abc import abstractmethod
2
- from ...utils import Table
3
+ from typing import TYPE_CHECKING
3
4
 
4
- from ...common.interfaces import IQuery
5
+ if TYPE_CHECKING:
6
+ from ormlambda import Table
7
+
8
+ from ormlambda.common.interfaces import IQuery
5
9
 
6
10
 
7
11
  class AbstractWhere(IQuery):
@@ -11,3 +11,4 @@ from .select import SelectQuery # noqa: F401
11
11
  from .update import UpdateQuery # noqa: F401
12
12
  from .upsert import UpsertQuery # noqa: F401
13
13
  from .where_condition import WhereCondition # noqa: F401
14
+ from .count import CountQuery # noqa: F401
@@ -0,0 +1,35 @@
1
+ from typing import Callable, Type, override
2
+
3
+ from ormlambda import Table, JoinType, ForeignKey
4
+ from ormlambda.databases.my_sql.clauses.joins import JoinSelector
5
+ from ormlambda.databases.my_sql.clauses.select import SelectQuery
6
+
7
+
8
+ class CountQuery[T: Type[Table]](SelectQuery[T]):
9
+ CLAUSE: str = "COUNT"
10
+
11
+ def __init__(
12
+ self,
13
+ tables: T | tuple[T] = (),
14
+ select_lambda: Callable[[T], None] | None = lambda: None,
15
+ *,
16
+ by: JoinType = JoinType.INNER_JOIN,
17
+ ) -> None:
18
+ super().__init__(tables, select_lambda, by=by)
19
+
20
+ @override
21
+ @property
22
+ def query(self) -> str:
23
+ query: str = f"{self.SELECT} {self.CLAUSE}(*) FROM {self._first_table.__table_name__}"
24
+
25
+ involved_tables = self.get_involved_tables()
26
+ if not involved_tables:
27
+ return query
28
+
29
+ sub_query: str = ""
30
+ for l_tbl, r_tbl in involved_tables:
31
+ join = JoinSelector(l_tbl, r_tbl, by=self._by, where=ForeignKey.MAPPED[l_tbl.__table_name__].referenced_tables[r_tbl.__table_name__].relationship)
32
+ sub_query += f" {join.query}"
33
+
34
+ query += sub_query
35
+ return query
@@ -1,8 +1,8 @@
1
1
  from typing import Literal, override
2
- from mysql.connector import Error, errorcode
2
+ from mysql.connector import errorcode, errors
3
3
  from mysql.connector import MySQLConnection
4
4
 
5
- from ....common.interfaces import IRepositoryBase
5
+ from ormlambda import IRepositoryBase
6
6
 
7
7
  TypeExists = Literal["fail", "replace", "append"]
8
8
 
@@ -18,12 +18,19 @@ class CreateDatabase:
18
18
 
19
19
  @override
20
20
  def execute(self, name: str, if_exists: TypeExists = "fail") -> None:
21
- with self._repository.connection.cursor() as cursor:
22
- try:
23
- cursor.execute(f"{self.CLAUSE} {name} DEFAULT CHARACTER SET 'utf8'")
24
- except Error as err:
25
- if err.errno == errorcode.ER_DB_CREATE_EXISTS and if_exists != "fail":
26
- cursor.execute(f"USE {name};")
27
- else:
28
- raise err
21
+ if self._repository.database_exists(name):
22
+ if if_exists == "replace":
23
+ self._repository.drop_database(name)
24
+ elif if_exists == "fail":
25
+ raise errors.DatabaseError(msg=f"Database '{name}' already exists", errno=errorcode.ER_DB_CREATE_EXISTS)
26
+ elif if_exists == "append":
27
+ counter: int = 0
28
+ char: str = ""
29
+ while self._repository.database_exists(name + char):
30
+ counter += 1
31
+ char = f"_{counter}"
32
+ name += char
33
+
34
+ query = f"{self.CLAUSE} {name} DEFAULT CHARACTER SET 'utf8'"
35
+ self._repository.execute(query)
29
36
  return None
@@ -1,8 +1,11 @@
1
- from typing import Any, override, Iterable
1
+ from typing import Any, override, Iterable, TYPE_CHECKING
2
2
 
3
- from ....utils import Table, Column
4
- from ....common.interfaces import IRepositoryBase
5
- from ....components.delete import DeleteQueryBase
3
+ if TYPE_CHECKING:
4
+ from ormlambda import Column
5
+
6
+ from ormlambda import Table
7
+ from ormlambda import IRepositoryBase
8
+ from ormlambda.components.delete import DeleteQueryBase
6
9
  from mysql.connector import MySQLConnection
7
10
 
8
11
 
@@ -2,7 +2,7 @@ from typing import override
2
2
  from mysql.connector import MySQLConnection
3
3
 
4
4
 
5
- from ....common.interfaces import IRepositoryBase
5
+ from ormlambda import IRepositoryBase
6
6
 
7
7
 
8
8
  class DropDatabase:
@@ -1,6 +1,6 @@
1
1
  from typing import Literal, override
2
2
 
3
- from ....common.interfaces import IRepositoryBase
3
+ from ormlambda import IRepositoryBase
4
4
 
5
5
  from mysql.connector import MySQLConnection
6
6
 
@@ -1,9 +1,10 @@
1
1
  from typing import Any, override, Iterable
2
2
  from mysql.connector import MySQLConnection
3
3
 
4
- from ....utils import Table, Column
5
- from ....components.insert import InsertQueryBase
6
- from ....common.interfaces import IRepositoryBase
4
+ from ormlambda import Table
5
+ from ormlambda import Column
6
+ from ormlambda.components.insert import InsertQueryBase
7
+ from ormlambda import IRepositoryBase
7
8
 
8
9
 
9
10
  class InsertQuery[T: Table](InsertQueryBase[T, IRepositoryBase[MySQLConnection]]):
@@ -1,13 +1,14 @@
1
- from typing import override, Callable, overload, Optional, TypeVar
1
+ from __future__ import annotations
2
+ from typing import override, Callable, overload, Optional, TYPE_CHECKING
2
3
 
3
- # from ..table import Table
4
4
 
5
- from ....common.interfaces.IQueryCommand import IQuery
6
- from ....utils.lambda_disassembler import Disassembler
7
- from ....common.enums import JoinType
5
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
6
+ from ormlambda import Disassembler
7
+ from ormlambda import JoinType
8
8
 
9
- # TODOL: Try to import Table module without circular import Error
10
- Table = TypeVar("Table")
9
+ # TODOL [x]: Try to import Table module without circular import Error
10
+ if TYPE_CHECKING:
11
+ from ormlambda import Table
11
12
 
12
13
 
13
14
  class JoinSelector[TLeft, TRight](IQuery):
@@ -1,6 +1,6 @@
1
1
  from typing import override
2
2
 
3
- from ....common.interfaces.IQueryCommand import IQuery
3
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
4
4
 
5
5
 
6
6
  class LimitQuery(IQuery):
@@ -1,6 +1,6 @@
1
1
  from typing import override
2
2
 
3
- from ....common.interfaces.IQueryCommand import IQuery
3
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
4
4
 
5
5
 
6
6
  class OffsetQuery(IQuery):
@@ -1,8 +1,8 @@
1
1
  from typing import override, Callable
2
2
 
3
- from ....utils.lambda_disassembler.tree_instruction import TreeInstruction
4
- from ....common.interfaces.IQueryCommand import IQuery
5
- from ....common.interfaces.IStatements import OrderType
3
+ from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction
4
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
5
+ from ormlambda.common.interfaces.IStatements import OrderType
6
6
 
7
7
  ASC = "ASC"
8
8
  DESC = "DESC"
@@ -1,10 +1,10 @@
1
1
  from typing import Callable, Optional, Type, override
2
2
  import inspect
3
3
 
4
- from ....utils.lambda_disassembler import TreeInstruction, TupleInstruction, NestedElement
5
- from ....components.select import ISelect, TableColumn
6
- from ....utils import Table, ForeignKey
7
- from ....utils.table_constructor import TableMeta
4
+ from ormlambda.utils.lambda_disassembler import TreeInstruction, TupleInstruction, NestedElement
5
+ from ormlambda.components.select import ISelect, TableColumn
6
+ from ormlambda import Table, ForeignKey
7
+ from ormlambda.utils.table_constructor import TableMeta
8
8
 
9
9
  from . import JoinSelector, JoinType
10
10
 
@@ -152,7 +152,7 @@ class SelectQuery[T: Table, *Ts](ISelect):
152
152
 
153
153
  sub_query: str = ""
154
154
  for l_tbl, r_tbl in involved_tables:
155
- join = JoinSelector(l_tbl, r_tbl, by=self._by, where=ForeignKey.MAPPED[l_tbl][r_tbl])
155
+ join = JoinSelector(l_tbl, r_tbl, by=self._by, where=ForeignKey.MAPPED[l_tbl.__table_name__].referenced_tables[r_tbl.__table_name__].relationship)
156
156
  sub_query += f" {join.query}"
157
157
 
158
158
  query += sub_query
@@ -1,9 +1,9 @@
1
1
  from typing import Type, override, Any
2
2
  from mysql.connector import MySQLConnection
3
3
 
4
- from ....components.update import UpdateQueryBase
5
- from ....utils import Table, Column
6
- from ....common.interfaces import IRepositoryBase
4
+ from ormlambda.components.update import UpdateQueryBase
5
+ from ormlambda import Table, Column
6
+ from ormlambda import IRepositoryBase
7
7
  from .where_condition import WhereCondition
8
8
 
9
9
 
@@ -1,8 +1,8 @@
1
1
  from typing import override, Any
2
2
 
3
- from ....utils import Table
4
- from ....components.upsert import UpsertQueryBase
5
- from ....common.interfaces import IRepositoryBase
3
+ from ormlambda import Table
4
+ from ormlambda.components.upsert import UpsertQueryBase
5
+ from ormlambda import IRepositoryBase
6
6
  from mysql.connector import MySQLConnection
7
7
 
8
8
  from .insert import InsertQuery
@@ -1,11 +1,11 @@
1
1
  from typing import Any, Callable, Optional, override
2
2
  import inspect
3
3
 
4
- from ....common.enums import ConditionType
5
- from ....utils.lambda_disassembler.tree_instruction import TreeInstruction, TupleInstruction
6
- from ....common.interfaces.IQueryCommand import IQuery
7
- from ....components.where.abstract_where import AbstractWhere
8
- from ....utils import Table
4
+ from ormlambda.common.enums import ConditionType
5
+ from ormlambda.utils.lambda_disassembler.tree_instruction import TreeInstruction, TupleInstruction
6
+ from ormlambda.common.interfaces.IQueryCommand import IQuery
7
+ from ormlambda.components.where.abstract_where import AbstractWhere
8
+ from ormlambda import Table
9
9
 
10
10
 
11
11
  class WhereConditionByArg[TProp1, TProp2](IQuery):
@@ -1,14 +1,15 @@
1
- # Standard libraries
1
+ from __future__ import annotations
2
2
  from pathlib import Path
3
- from typing import Any, Type, override
4
-
3
+ from typing import Any, Optional, Type, override, Callable
4
+ import functools
5
5
 
6
6
  # from mysql.connector.pooling import MySQLConnectionPool
7
7
  from mysql.connector import MySQLConnection, Error # noqa: F401
8
+ from mysql.connector.pooling import PooledMySQLConnection, MySQLConnectionPool # noqa: F401
8
9
 
9
10
  # Custom libraries
10
- from ...common.interfaces import IRepositoryBase
11
- from ...utils.module_tree.dynamic_module import ModuleTree
11
+ from ormlambda import IRepositoryBase
12
+ from ormlambda.utils.module_tree.dynamic_module import ModuleTree
12
13
 
13
14
  from .clauses import CreateDatabase, TypeExists
14
15
  from .clauses import DropDatabase
@@ -51,39 +52,66 @@ class Response[TFlavour, *Ts]:
51
52
  def _tuple() -> list[tuple[*Ts]]:
52
53
  return data
53
54
 
55
+ def _set() -> list[set]:
56
+ for d in data:
57
+ n = len(d)
58
+ for i in range(n):
59
+ try:
60
+ hash(d[i])
61
+ except TypeError:
62
+ raise TypeError(f"unhashable type '{type(d[i])}' found in '{type(d)}' when attempting to cast the result into a '{set.__name__}' object")
63
+ return [set(x) for x in data]
64
+
54
65
  def _default() -> list[TFlavour]:
55
66
  return [self._flavour(x, **self._kwargs) for x in data]
56
67
 
57
- selector: dict[Type[object], Any] = {dict: _dict, tuple: _tuple}
68
+ selector: dict[Type[object], Any] = {
69
+ dict: _dict,
70
+ tuple: _tuple,
71
+ set: _set,
72
+ }
58
73
 
59
74
  return selector.get(self._flavour, _default)()
60
75
 
61
76
 
62
77
  class MySQLRepository(IRepositoryBase[MySQLConnection]):
78
+ def get_connection(func: Callable[..., Any]):
79
+ @functools.wraps(func)
80
+ def wrapper(self: IRepositoryBase[MySQLConnection], *args, **kwargs):
81
+ self.connect()
82
+ try:
83
+ foo = func(self, *args, **kwargs)
84
+ finally:
85
+ self.connection.rollback()
86
+ self.close_connection()
87
+ return foo
88
+
89
+ return wrapper
90
+
63
91
  def __init__(self, **kwargs: Any) -> None:
64
92
  self._data_config: dict[str, Any] = kwargs
65
- self._connection: MySQLConnection = MySQLConnection()
66
- # self._pool_connection: MySQLConnection = MySQLConnection(**kwargs)
67
- pass
93
+ self._pool: MySQLConnectionPool = self.__create_MySQLConnectionPool()
94
+ self._connection: PooledMySQLConnection = None
68
95
 
96
+ def __create_MySQLConnectionPool(self):
97
+ return MySQLConnectionPool(pool_name="mypool",pool_size=10, **self._data_config)
69
98
  @override
70
99
  def is_connected(self) -> bool:
71
- return self._connection.is_connected()
100
+ return self._connection._cnx is not None if self._connection else False
72
101
 
73
102
  @override
74
- def connect(self) -> IRepositoryBase[MySQLConnection]:
75
- # return MySQLConnectionPool(pool_name="mypool", pool_size=5, **kwargs)
76
- self._connection.connect(**self._data_config)
103
+ def connect(self) -> None:
104
+ self._connection = self._pool.get_connection()
77
105
  return None
78
106
 
79
107
  @override
80
108
  def close_connection(self) -> None:
81
- if self._connection.is_connected():
109
+ if self.is_connected():
82
110
  self._connection.close()
83
111
  return None
84
112
 
85
113
  @override
86
- @IRepositoryBase.check_connection
114
+ @get_connection
87
115
  def read_sql[TFlavour](self, query: str, flavour: Type[TFlavour] = tuple, **kwargs) -> tuple[TFlavour]:
88
116
  """
89
117
  Return tuple of tuples by default.
@@ -101,7 +129,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
101
129
  return Response[TFlavour](response_values=values, columns=columns, flavour=flavour, **kwargs).response
102
130
 
103
131
  # FIXME [ ]: this method does not comply with the implemented interface
104
- @IRepositoryBase.check_connection
132
+ @get_connection
105
133
  def create_tables_code_first(self, path: str | Path) -> None:
106
134
  if not isinstance(path, Path | str):
107
135
  raise ValueError
@@ -123,7 +151,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
123
151
  return None
124
152
 
125
153
  @override
126
- @IRepositoryBase.check_connection
154
+ @get_connection
127
155
  def executemany_with_values(self, query: str, values) -> None:
128
156
  with self._connection.cursor(buffered=True) as cursor:
129
157
  cursor.executemany(query, values)
@@ -131,7 +159,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
131
159
  return None
132
160
 
133
161
  @override
134
- @IRepositoryBase.check_connection
162
+ @get_connection
135
163
  def execute_with_values(self, query: str, values) -> None:
136
164
  with self._connection.cursor(buffered=True) as cursor:
137
165
  cursor.execute(query, values)
@@ -139,7 +167,7 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
139
167
  return None
140
168
 
141
169
  @override
142
- @IRepositoryBase.check_connection
170
+ @get_connection
143
171
  def execute(self, query: str) -> None:
144
172
  with self._connection.cursor(buffered=True) as cursor:
145
173
  cursor.execute(query)
@@ -147,12 +175,11 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
147
175
  return None
148
176
 
149
177
  @override
150
- @IRepositoryBase.check_connection
151
178
  def drop_table(self, name: str) -> None:
152
179
  return DropTable(self).execute(name)
153
180
 
154
181
  @override
155
- @IRepositoryBase.check_connection
182
+ @get_connection
156
183
  def database_exists(self, name: str) -> bool:
157
184
  query = "SHOW DATABASES LIKE %s;"
158
185
  with self._connection.cursor(buffered=True) as cursor:
@@ -161,12 +188,11 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
161
188
  return len(res) > 0
162
189
 
163
190
  @override
164
- @IRepositoryBase.check_connection
165
191
  def drop_database(self, name: str) -> None:
166
192
  return DropDatabase(self).execute(name)
167
193
 
168
194
  @override
169
- @IRepositoryBase.check_connection
195
+ @get_connection
170
196
  def table_exists(self, name: str) -> bool:
171
197
  if not self._connection.database:
172
198
  raise Exception("No database selected")
@@ -178,7 +204,6 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
178
204
  return len(res) > 0
179
205
 
180
206
  @override
181
- @IRepositoryBase.check_connection
182
207
  def create_database(self, name: str, if_exists: TypeExists = "fail") -> None:
183
208
  return CreateDatabase(self).execute(name, if_exists)
184
209
 
@@ -187,6 +212,11 @@ class MySQLRepository(IRepositoryBase[MySQLConnection]):
187
212
  def connection(self) -> MySQLConnection:
188
213
  return self._connection
189
214
 
190
- @override
191
- def set_config(self, value: dict[str, Any]) -> dict[str, Any]:
192
- return self._data_config.update(value)
215
+ @property
216
+ def database(self) -> Optional[str]:
217
+ return self._data_config.get("database", None)
218
+
219
+ @database.setter
220
+ def database(self, value: str) -> None:
221
+ self._data_config["database"] = value
222
+ self._pool = self.__create_MySQLConnectionPool()