ormlambda 3.12.2__py3-none-any.whl → 3.34.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 (150) hide show
  1. ormlambda/__init__.py +2 -0
  2. ormlambda/caster/__init__.py +1 -1
  3. ormlambda/caster/caster.py +29 -12
  4. ormlambda/common/abstract_classes/clause_info_converter.py +4 -12
  5. ormlambda/common/abstract_classes/decomposition_query.py +17 -2
  6. ormlambda/common/abstract_classes/non_query_base.py +9 -7
  7. ormlambda/common/abstract_classes/query_base.py +3 -1
  8. ormlambda/common/errors/__init__.py +29 -0
  9. ormlambda/common/interfaces/IQueryCommand.py +6 -2
  10. ormlambda/dialects/__init__.py +39 -0
  11. ormlambda/dialects/default/__init__.py +1 -0
  12. ormlambda/dialects/default/base.py +39 -0
  13. ormlambda/dialects/interface/__init__.py +1 -0
  14. ormlambda/dialects/interface/dialect.py +78 -0
  15. ormlambda/dialects/mysql/__init__.py +38 -0
  16. ormlambda/dialects/mysql/base.py +388 -0
  17. ormlambda/dialects/mysql/caster/caster.py +39 -0
  18. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/__init__.py +1 -0
  19. ormlambda/dialects/mysql/caster/types/boolean.py +35 -0
  20. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/bytes.py +7 -7
  21. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/datetime.py +7 -7
  22. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/float.py +7 -7
  23. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/int.py +7 -7
  24. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/iterable.py +7 -7
  25. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/none.py +8 -7
  26. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/point.py +4 -4
  27. ormlambda/{databases/my_sql → dialects/mysql}/caster/types/string.py +7 -7
  28. ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_AsText.py +8 -7
  29. ormlambda/{databases/my_sql → dialects/mysql}/clauses/ST_Contains.py +10 -5
  30. ormlambda/dialects/mysql/clauses/__init__.py +13 -0
  31. ormlambda/dialects/mysql/clauses/count.py +33 -0
  32. ormlambda/dialects/mysql/clauses/delete.py +9 -0
  33. ormlambda/dialects/mysql/clauses/group_by.py +17 -0
  34. ormlambda/dialects/mysql/clauses/having.py +12 -0
  35. ormlambda/dialects/mysql/clauses/insert.py +9 -0
  36. ormlambda/dialects/mysql/clauses/joins.py +14 -0
  37. ormlambda/dialects/mysql/clauses/limit.py +6 -0
  38. ormlambda/dialects/mysql/clauses/offset.py +6 -0
  39. ormlambda/dialects/mysql/clauses/order.py +8 -0
  40. ormlambda/dialects/mysql/clauses/update.py +8 -0
  41. ormlambda/dialects/mysql/clauses/upsert.py +9 -0
  42. ormlambda/dialects/mysql/clauses/where.py +7 -0
  43. ormlambda/dialects/mysql/mysqlconnector.py +46 -0
  44. ormlambda/dialects/mysql/repository/__init__.py +1 -0
  45. ormlambda/dialects/mysql/repository/repository.py +212 -0
  46. ormlambda/dialects/mysql/types.py +732 -0
  47. ormlambda/dialects/sqlite/__init__.py +5 -0
  48. ormlambda/dialects/sqlite/base.py +47 -0
  49. ormlambda/dialects/sqlite/pysqlite.py +32 -0
  50. ormlambda/engine/__init__.py +1 -0
  51. ormlambda/engine/base.py +77 -0
  52. ormlambda/engine/create.py +9 -23
  53. ormlambda/engine/url.py +31 -19
  54. ormlambda/env.py +30 -0
  55. ormlambda/errors.py +17 -0
  56. ormlambda/model/base_model.py +7 -9
  57. ormlambda/repository/base_repository.py +36 -5
  58. ormlambda/repository/interfaces/IRepositoryBase.py +119 -12
  59. ormlambda/repository/response.py +134 -0
  60. ormlambda/sql/clause_info/aggregate_function_base.py +19 -9
  61. ormlambda/sql/clause_info/clause_info.py +34 -17
  62. ormlambda/sql/clauses/__init__.py +14 -0
  63. ormlambda/{databases/my_sql → sql}/clauses/alias.py +23 -6
  64. ormlambda/{databases/my_sql → sql}/clauses/count.py +15 -1
  65. ormlambda/{databases/my_sql → sql}/clauses/delete.py +22 -7
  66. ormlambda/sql/clauses/group_by.py +30 -0
  67. ormlambda/{databases/my_sql → sql}/clauses/having.py +7 -2
  68. ormlambda/{databases/my_sql → sql}/clauses/insert.py +16 -9
  69. ormlambda/sql/clauses/interfaces/__init__.py +5 -0
  70. ormlambda/{components → sql/clauses}/join/join_context.py +15 -7
  71. ormlambda/{databases/my_sql → sql}/clauses/joins.py +29 -19
  72. ormlambda/sql/clauses/limit.py +15 -0
  73. ormlambda/sql/clauses/offset.py +15 -0
  74. ormlambda/{databases/my_sql → sql}/clauses/order.py +14 -24
  75. ormlambda/{databases/my_sql → sql}/clauses/select.py +12 -13
  76. ormlambda/{databases/my_sql → sql}/clauses/update.py +24 -11
  77. ormlambda/{databases/my_sql → sql}/clauses/upsert.py +17 -12
  78. ormlambda/{databases/my_sql → sql}/clauses/where.py +28 -8
  79. ormlambda/sql/column/__init__.py +1 -0
  80. ormlambda/sql/{column.py → column/column.py} +82 -22
  81. ormlambda/sql/comparer.py +51 -37
  82. ormlambda/sql/compiler.py +668 -0
  83. ormlambda/sql/ddl.py +82 -0
  84. ormlambda/sql/elements.py +36 -0
  85. ormlambda/sql/foreign_key.py +61 -39
  86. ormlambda/{databases/my_sql → sql}/functions/concat.py +13 -5
  87. ormlambda/{databases/my_sql → sql}/functions/max.py +9 -4
  88. ormlambda/{databases/my_sql → sql}/functions/min.py +9 -13
  89. ormlambda/{databases/my_sql → sql}/functions/sum.py +8 -10
  90. ormlambda/sql/sqltypes.py +647 -0
  91. ormlambda/sql/table/__init__.py +1 -1
  92. ormlambda/sql/table/table.py +175 -0
  93. ormlambda/sql/table/table_constructor.py +1 -208
  94. ormlambda/sql/type_api.py +35 -0
  95. ormlambda/sql/types.py +3 -1
  96. ormlambda/sql/visitors.py +74 -0
  97. ormlambda/statements/__init__.py +1 -0
  98. ormlambda/statements/base_statement.py +28 -38
  99. ormlambda/statements/interfaces/IStatements.py +8 -4
  100. ormlambda/{databases/my_sql → statements}/query_builder.py +35 -30
  101. ormlambda/{databases/my_sql → statements}/statements.py +57 -61
  102. ormlambda/statements/types.py +2 -2
  103. ormlambda/types/__init__.py +24 -0
  104. ormlambda/types/metadata.py +42 -0
  105. ormlambda/util/__init__.py +87 -0
  106. ormlambda/{utils → util}/module_tree/dynamic_module.py +1 -1
  107. ormlambda/util/plugin_loader.py +32 -0
  108. ormlambda/util/typing.py +6 -0
  109. ormlambda-3.34.0.dist-info/AUTHORS +32 -0
  110. {ormlambda-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/METADATA +1 -1
  111. ormlambda-3.34.0.dist-info/RECORD +152 -0
  112. ormlambda/components/__init__.py +0 -4
  113. ormlambda/components/delete/__init__.py +0 -2
  114. ormlambda/components/delete/abstract_delete.py +0 -17
  115. ormlambda/components/insert/__init__.py +0 -2
  116. ormlambda/components/insert/abstract_insert.py +0 -25
  117. ormlambda/components/select/__init__.py +0 -1
  118. ormlambda/components/update/__init__.py +0 -2
  119. ormlambda/components/update/abstract_update.py +0 -29
  120. ormlambda/components/upsert/__init__.py +0 -2
  121. ormlambda/components/upsert/abstract_upsert.py +0 -25
  122. ormlambda/databases/__init__.py +0 -5
  123. ormlambda/databases/my_sql/__init__.py +0 -4
  124. ormlambda/databases/my_sql/caster/caster.py +0 -39
  125. ormlambda/databases/my_sql/clauses/__init__.py +0 -20
  126. ormlambda/databases/my_sql/clauses/create_database.py +0 -35
  127. ormlambda/databases/my_sql/clauses/drop_database.py +0 -17
  128. ormlambda/databases/my_sql/clauses/drop_table.py +0 -26
  129. ormlambda/databases/my_sql/clauses/group_by.py +0 -30
  130. ormlambda/databases/my_sql/clauses/limit.py +0 -17
  131. ormlambda/databases/my_sql/clauses/offset.py +0 -17
  132. ormlambda/databases/my_sql/repository/__init__.py +0 -1
  133. ormlambda/databases/my_sql/repository/repository.py +0 -351
  134. ormlambda/engine/template.py +0 -47
  135. ormlambda/sql/dtypes.py +0 -94
  136. ormlambda/utils/__init__.py +0 -1
  137. ormlambda-3.12.2.dist-info/RECORD +0 -125
  138. /ormlambda/{databases/my_sql → dialects/mysql}/caster/__init__.py +0 -0
  139. /ormlambda/{databases/my_sql/types.py → dialects/mysql/repository/pool_types.py} +0 -0
  140. /ormlambda/{components/delete → sql/clauses/interfaces}/IDelete.py +0 -0
  141. /ormlambda/{components/insert → sql/clauses/interfaces}/IInsert.py +0 -0
  142. /ormlambda/{components/select → sql/clauses/interfaces}/ISelect.py +0 -0
  143. /ormlambda/{components/update → sql/clauses/interfaces}/IUpdate.py +0 -0
  144. /ormlambda/{components/upsert → sql/clauses/interfaces}/IUpsert.py +0 -0
  145. /ormlambda/{components → sql/clauses}/join/__init__.py +0 -0
  146. /ormlambda/{databases/my_sql → sql}/functions/__init__.py +0 -0
  147. /ormlambda/{utils → util}/module_tree/__init__.py +0 -0
  148. /ormlambda/{utils → util}/module_tree/dfs_traversal.py +0 -0
  149. {ormlambda-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/LICENSE +0 -0
  150. {ormlambda-3.12.2.dist-info → ormlambda-3.34.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,5 @@
1
+ from . import base
2
+ from . import pysqlite
3
+
4
+ # default dialect
5
+ base.dialect = dialect = pysqlite.dialect
@@ -0,0 +1,47 @@
1
+ from ormlambda.sql import compiler
2
+ from .. import default
3
+
4
+
5
+ class SQLiteCompiler(compiler.SQLCompiler):
6
+ render_table_with_column_in_update_from = True
7
+ """Overridden from base SQLCompiler value"""
8
+
9
+
10
+ class SQLiteDDLCompiler(compiler.DDLCompiler): ...
11
+
12
+
13
+ class SQLiteTypeCompiler(compiler.GenericTypeCompiler):
14
+ def visit_INTEGER(self, type_, **kw):
15
+ if self._mysql_type(type_) and type_.display_width is not None:
16
+ return self._extend_numeric(
17
+ type_,
18
+ "INTEGER(%(display_width)s)" % {"display_width": type_.display_width},
19
+ )
20
+ else:
21
+ return self._extend_numeric(type_, "INTEGER")
22
+
23
+ def visit_VARCHAR(self, type_, **kw):
24
+ if type_.length is None:
25
+ raise ValueError("VARCHAR requires a length on dialect %s" % self.dialect.name)
26
+ return self._extend_string(type_, {}, "VARCHAR(%d)" % type_.length)
27
+
28
+ def visit_CHAR(self, type_, **kw):
29
+ if type_.length is not None:
30
+ return self._extend_string(type_, {}, "CHAR(%(length)s)" % {"length": type_.length})
31
+ else:
32
+ return self._extend_string(type_, {}, "CHAR")
33
+
34
+
35
+ class SQLiteDialect(default.DefaultDialect):
36
+ """Details of the SQLite dialect.
37
+ Not used directly in application code.
38
+ """
39
+
40
+ name = "mysql"
41
+
42
+ statement_compiler = SQLiteCompiler
43
+ ddl_compiler = SQLiteDDLCompiler
44
+ type_compiler_cls = SQLiteTypeCompiler
45
+
46
+ def __init__(self, **kwargs):
47
+ super().__init__(**kwargs)
@@ -0,0 +1,32 @@
1
+ from types import ModuleType
2
+ from ..default import DefaultDialect
3
+ from ormlambda.sql import compiler
4
+
5
+
6
+ class SQLiteCompiler(compiler.SQLCompiler): ...
7
+
8
+
9
+ class SQLiteDDLCompiler(compiler.DDLCompiler): ...
10
+
11
+
12
+ class SQLiteTypeCompiler(compiler.TypeCompiler): ...
13
+
14
+
15
+ class SQLiteDialect_pysqlite(DefaultDialect):
16
+ name = "sqlite"
17
+
18
+ statement_compiler = SQLiteCompiler
19
+ ddl_compiler = SQLiteDDLCompiler
20
+ type_compiler_cls = SQLiteTypeCompiler
21
+
22
+ def __init__(self, **kwargs):
23
+ super().__init__(**kwargs)
24
+
25
+ @classmethod
26
+ def import_dbapi(cls) -> ModuleType:
27
+ from sqlite3 import dbapi2 as sqlite
28
+
29
+ return sqlite
30
+
31
+
32
+ dialect = SQLiteDialect_pysqlite
@@ -1,2 +1,3 @@
1
1
  from .create import create_engine # noqa: F401
2
2
  from .url import URL, make_url # noqa: F401
3
+ from .base import Engine # noqa: F401
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+ from pathlib import Path
3
+ from typing import TYPE_CHECKING, BinaryIO, Literal, Optional, TextIO
4
+ from ormlambda.engine import url
5
+ from ormlambda.sql.ddl import CreateSchema, DropSchema, CreateBackup
6
+
7
+ if TYPE_CHECKING:
8
+ from ormlambda.dialects import Dialect
9
+
10
+ type TypeExists = Literal["fail", "replace", "append"]
11
+
12
+
13
+ class Engine:
14
+ def __init__(self, dialect: Dialect, url: url.URL):
15
+ self.dialect = dialect
16
+ self.url = url
17
+ self.repository = self.dialect.repository_cls(url, dialect=dialect)
18
+
19
+ def create_schema(self, schema_name: str, if_exists: TypeExists = "fail") -> None:
20
+ if if_exists == "replace":
21
+ self.drop_schema(schema_name, if_exists)
22
+
23
+ sql = CreateSchema(schema=schema_name, if_not_exists=if_exists == "append").compile(self.dialect).string
24
+ try:
25
+ self.repository.execute(sql)
26
+ except Exception:
27
+ if if_exists == "fail":
28
+ raise
29
+
30
+ def drop_schema(self, schema_name: str, if_exists: bool = False) -> None:
31
+ """
32
+ Generate a DROP SCHEMA SQL statement for MySQL.
33
+
34
+ Args:
35
+ schema_name (str): Name of the schema/database to drop
36
+ if_exists (bool): Whether to include IF EXISTS clause
37
+
38
+ Returns:
39
+ str: The SQL DROP SCHEMA statement
40
+
41
+ Raises:
42
+ ValueError: If schema_name is empty or contains invalid characters
43
+ """
44
+ # if not schema_name or not isinstance(schema_name, str):
45
+ # raise ValueError("Schema name must be a non-empty string")
46
+
47
+ sql = DropSchema(schema=schema_name, if_exists=if_exists).compile(self.dialect).string
48
+ return self.repository.execute(sql)
49
+
50
+ def schema_exists(self, schema: str) -> bool:
51
+ # sql = SchemaExists(schema).compile(self.dialect).string
52
+ # return bool(res and len(res) > 0)
53
+
54
+ return self.repository.database_exists(schema)
55
+
56
+ def set_database(self, name: str) -> None:
57
+ self.repository.database = name
58
+ return None
59
+
60
+ def create_backup(
61
+ self,
62
+ output: Optional[str | BinaryIO | TextIO] = None,
63
+ compress: bool = False,
64
+ backup_dir: str = "backups",
65
+ **kw,
66
+ ) -> Optional[str | BinaryIO | Path]:
67
+ return (
68
+ CreateBackup(self.url)
69
+ .compile(
70
+ self.dialect,
71
+ output=output,
72
+ compress=compress,
73
+ backup_dir=backup_dir,
74
+ **kw,
75
+ )
76
+ .string
77
+ )
@@ -1,35 +1,21 @@
1
+ from __future__ import annotations
1
2
  from typing import Any
2
3
 
3
4
  from ormlambda.engine.url import URL, make_url
4
- from ormlambda import BaseRepository
5
5
 
6
+ from . import base
6
7
 
7
- def create_engine(url: URL | str, **kwargs: Any) -> BaseRepository:
8
- from ormlambda.databases import MySQLRepository
9
8
 
9
+ def create_engine(url: URL | str, **kwargs: Any) -> base.Engine:
10
10
  # create url.URL object
11
11
  u = make_url(url)
12
12
  url, kwargs = u._instantiate_plugins(kwargs)
13
13
 
14
- repo_selector = {
15
- "mysql": MySQLRepository,
16
- }
14
+ entrypoint = u._get_entrypoint()
15
+ dialect_cls = entrypoint.get_dialect_cls()
17
16
 
18
- if url.drivername not in repo_selector:
19
- raise ValueError(f"drivername '{url.drivername}' not expected to load Repository class")
17
+ dialect_args = {}
18
+ dialect_args["dbapi"] = dialect_cls.import_dbapi()
20
19
 
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)
20
+ dialect = dialect_cls(**dialect_args)
21
+ return base.Engine(dialect, u)
ormlambda/engine/url.py CHANGED
@@ -1,23 +1,16 @@
1
- """
2
- URL class extracted from SQLAlchemy
3
- """
1
+ # engine/url.py
2
+ # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ #
4
8
 
5
9
  from __future__ import annotations
6
10
 
7
11
  import re
8
12
  import collections.abc as collections_abc
9
- from typing import (
10
- Any,
11
- Literal,
12
- cast,
13
- Iterable,
14
- Mapping,
15
- NamedTuple,
16
- Optional,
17
- overload,
18
- Sequence,
19
- Union,
20
- )
13
+ from typing import Any, Literal, Type, cast, Iterable, Mapping, NamedTuple, Optional, overload, Sequence, Union, TYPE_CHECKING
21
14
 
22
15
  from urllib.parse import (
23
16
  parse_qsl,
@@ -28,7 +21,12 @@ from urllib.parse import (
28
21
 
29
22
  from . import utils
30
23
 
31
- type DrivernameType = Literal["mysql"] | str
24
+ from ..dialects import registry
25
+
26
+ if TYPE_CHECKING:
27
+ from ..dialects import Dialect
28
+
29
+ type DrivernameType = Literal["mysql", "sqlite"] | str
32
30
 
33
31
 
34
32
  class URL(NamedTuple):
@@ -665,6 +663,20 @@ class URL(NamedTuple):
665
663
 
666
664
  return u, kwargs
667
665
 
666
+ def _get_entrypoint(self) -> Type[Dialect]:
667
+ """Return the dialect class for this URL.
668
+
669
+ This is the dialect class that corresponds to the database backend
670
+ in use, and is the portion of the :attr:`_engine.URL.drivername`
671
+ that is to the left of the plus sign.
672
+
673
+ """
674
+ if "+" not in self.drivername:
675
+ name = self.drivername
676
+ else:
677
+ name = self.drivername.replace("+", ".")
678
+ return registry.load(name)
679
+
668
680
 
669
681
  def make_url(name_or_url: str | URL) -> URL:
670
682
  """Given a string, produce a new URL instance.
@@ -684,10 +696,10 @@ def make_url(name_or_url: str | URL) -> URL:
684
696
 
685
697
  if isinstance(name_or_url, str):
686
698
  return _parse_url(name_or_url)
687
- elif not isinstance(name_or_url, URL) and not hasattr(name_or_url, "_sqla_is_testing_if_this_is_a_mock_object"):
699
+ if not isinstance(name_or_url, URL) and not hasattr(name_or_url, "_sqla_is_testing_if_this_is_a_mock_object"):
688
700
  raise ValueError(f"Expected string or URL object, got {name_or_url!r}")
689
- else:
690
- return name_or_url
701
+
702
+ return name_or_url
691
703
 
692
704
 
693
705
  def _parse_url(name: str) -> URL:
ormlambda/env.py ADDED
@@ -0,0 +1,30 @@
1
+ import os
2
+ from pathlib import Path
3
+ import logging
4
+ import sys
5
+
6
+ ORMLAMBDA_DIR = Path(__file__).parent
7
+ SRC_DIR = ORMLAMBDA_DIR.parent
8
+ BASE_DIR = SRC_DIR.parent
9
+
10
+
11
+ try:
12
+ from dotenv import find_dotenv, load_dotenv
13
+
14
+ load_dotenv(find_dotenv(str(BASE_DIR / ".env")))
15
+
16
+ except ImportError:
17
+ print("dotenv not installed, skipping...")
18
+
19
+
20
+ GLOBAL_LOG_LEVEL = os.getenv("GLOBAL_LOG_LEVEL", "ERROR").upper()
21
+ logging.basicConfig(
22
+ level=logging.getLevelNamesMapping().get(GLOBAL_LOG_LEVEL, logging.ERROR),
23
+ handlers=[
24
+ logging.StreamHandler(sys.stdout),
25
+ logging.FileHandler(str(BASE_DIR / "errors.log"), "w", encoding="utf-8"),
26
+ ],
27
+ )
28
+
29
+ log = logging.getLogger(__name__)
30
+ log.info(f"GLOBAL_LOG_LEVEL: {GLOBAL_LOG_LEVEL}")
ormlambda/errors.py ADDED
@@ -0,0 +1,17 @@
1
+ class CompileError(Exception):
2
+ """Exception raised for errors in the compilation process."""
3
+
4
+ def __init__(self, message):
5
+ super().__init__(message)
6
+ self.message = message
7
+
8
+ def __str__(self):
9
+ return f"CompileError: {self.message}"
10
+
11
+
12
+ class NoSuchModuleError(Exception):
13
+ """Raised when a dynamically-loaded module (usually a database dialect)
14
+ of a particular name cannot be located."""
15
+
16
+ def __str__(self):
17
+ return f"NoSuchModuleError: {self.args[0]}"
@@ -3,11 +3,11 @@ from __future__ import annotations
3
3
 
4
4
  from typing import TYPE_CHECKING, Type
5
5
 
6
- from ormlambda.engine.template import RepositoryTemplateDict
6
+ from ormlambda.statements import Statements
7
+ from ormlambda.engine import Engine
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from ormlambda.statements.interfaces import IStatements_two_generic
10
- from ormlambda.repository import BaseRepository
11
11
 
12
12
 
13
13
  # endregion
@@ -22,16 +22,14 @@ class BaseModel[T]:
22
22
 
23
23
  # region Constructor
24
24
 
25
- def __new__[TPool](cls, model: Type[T], repository: BaseRepository[TPool]) -> IStatements_two_generic[T, TPool]:
26
- if repository is None:
25
+ def __new__[TPool](cls, model: Type[T], engine: Engine) -> IStatements_two_generic[T, TPool]:
26
+ if engine is None:
27
27
  raise ValueError("`None` cannot be passed to the `repository` attribute when calling the `BaseModel` class")
28
28
 
29
- new_cls = RepositoryTemplateDict().get(repository).statement
29
+ if not isinstance(engine, Engine):
30
+ raise ValueError(f"`{engine}` is not a valid `Engine` instance")
30
31
 
31
- if not new_cls:
32
- raise Exception(f"The selected repository '{repository}' does not exist.")
33
-
34
- return new_cls(model, repository)
32
+ return Statements(model, engine)
35
33
 
36
34
 
37
35
  ORM = BaseModel
@@ -1,13 +1,44 @@
1
- import contextlib
2
- from typing import Generator, Type, Unpack
1
+ from __future__ import annotations
2
+ from typing import Generator, Optional, Type, Unpack, TYPE_CHECKING
3
3
  from ormlambda.repository import IRepositoryBase
4
4
  import abc
5
5
 
6
+ if TYPE_CHECKING:
7
+ from ormlambda import URL as _URL
8
+ from ormlambda.dialects.interface.dialect import Dialect
9
+
6
10
 
7
11
  class BaseRepository[TPool](IRepositoryBase):
8
- def __init__[TArgs](self, pool: Type[TPool], **kwargs: Unpack[TArgs]) -> None:
9
- self._pool: TPool = pool(**kwargs)
12
+ def __init__[TArgs](
13
+ self,
14
+ /,
15
+ url: Optional[_URL] = None,
16
+ *,
17
+ dialect: Optional[Dialect] = None,
18
+ user: Optional[str] = None,
19
+ password: Optional[str] = None,
20
+ host: Optional[str] = None,
21
+ database: Optional[str] = None,
22
+ pool: Optional[Type[TPool]] = None,
23
+ **kwargs: Unpack[TArgs],
24
+ ) -> None:
25
+ self._dialect = dialect
26
+ self._url = url
27
+
28
+ self._user = user
29
+ self._password = password
30
+ self._host = host
31
+ self._database = database
32
+
33
+ if pool:
34
+ attr = {
35
+ "user": self._user,
36
+ "password": self._password,
37
+ "host": self._host,
38
+ "database": self._database,
39
+ **kwargs,
40
+ }
41
+ self._pool: TPool = pool(**attr)
10
42
 
11
- @contextlib.contextmanager
12
43
  @abc.abstractmethod
13
44
  def get_connection[TCnx](self) -> Generator[TCnx, None, None]: ...
@@ -1,9 +1,125 @@
1
1
  from __future__ import annotations
2
2
  from abc import ABC, abstractmethod
3
- from typing import Optional, Type, Iterable, TYPE_CHECKING
3
+ from typing import (
4
+ Annotated,
5
+ Any,
6
+ Mapping,
7
+ Optional,
8
+ Protocol,
9
+ Sequence,
10
+ Type,
11
+ Iterable,
12
+ )
4
13
 
5
- if TYPE_CHECKING:
6
- from ormlambda.statements.types import TypeExists
14
+
15
+ type _DBAPICursorDescription = Sequence[
16
+ tuple[
17
+ Annotated[str, "name"],
18
+ Annotated[str, "type_code"],
19
+ Annotated[Optional[str], "display_size"],
20
+ Annotated[Optional[str], "internal_size"],
21
+ Annotated[Optional[str], "precision"],
22
+ Annotated[Optional[str], "scale"],
23
+ Annotated[Optional[str], "null_ok"],
24
+ ]
25
+ ]
26
+
27
+
28
+ type _GenericSequence = Sequence[Any]
29
+
30
+ type _CoreSingleExecuteParams = Mapping[str, Any]
31
+ type _CoreMultiExecuteParams = Sequence[_CoreSingleExecuteParams]
32
+
33
+ type _DBAPISingleExecuteParams = _GenericSequence | _CoreSingleExecuteParams
34
+ type _DBAPIMultiExecuteParams = Sequence[_GenericSequence] | _CoreMultiExecuteParams
35
+
36
+
37
+ class DBAPICursor(Protocol):
38
+ """protocol representing a :pep:`249` database cursor.
39
+
40
+ .. versionadded:: 2.0
41
+
42
+ .. seealso::
43
+
44
+ `Cursor Objects <https://www.python.org/dev/peps/pep-0249/#cursor-objects>`_
45
+ - in :pep:`249`
46
+
47
+ """ # noqa: E501
48
+
49
+ @property
50
+ def description(
51
+ self,
52
+ ) -> _DBAPICursorDescription:
53
+ """The description attribute of the Cursor.
54
+
55
+ .. seealso::
56
+
57
+ `cursor.description <https://www.python.org/dev/peps/pep-0249/#description>`_
58
+ - in :pep:`249`
59
+
60
+
61
+ """ # noqa: E501
62
+ ...
63
+
64
+ @property
65
+ def rowcount(self) -> int: ...
66
+
67
+ arraysize: int
68
+
69
+ lastrowid: int
70
+
71
+ def close(self) -> None: ...
72
+
73
+ def execute(
74
+ self,
75
+ operation: Any,
76
+ parameters: Optional[_DBAPISingleExecuteParams] = None,
77
+ ) -> Any: ...
78
+
79
+ def executemany(
80
+ self,
81
+ operation: Any,
82
+ parameters: _DBAPIMultiExecuteParams,
83
+ ) -> Any: ...
84
+
85
+ def fetchone(self) -> Optional[Any]: ...
86
+
87
+ def fetchmany(self, size: int = ...) -> Sequence[Any]: ...
88
+
89
+ def fetchall(self) -> Sequence[Any]: ...
90
+
91
+ def setinputsizes(self, sizes: Sequence[Any]) -> None: ...
92
+
93
+ def setoutputsize(self, size: Any, column: Any) -> None: ...
94
+
95
+ def callproc(self, procname: str, parameters: Sequence[Any] = ...) -> Any: ...
96
+
97
+ def nextset(self) -> Optional[bool]: ...
98
+
99
+ def __getattr__(self, key: str) -> Any: ...
100
+
101
+
102
+ class DBAPIConnection(Protocol):
103
+ """protocol representing a :pep:`249` database connection.
104
+
105
+ .. versionadded:: 2.0
106
+
107
+ .. seealso::
108
+
109
+ `Connection Objects <https://www.python.org/dev/peps/pep-0249/#connection-objects>`_
110
+ - in :pep:`249`
111
+
112
+ """ # noqa: E501
113
+
114
+ def close(self) -> None: ...
115
+
116
+ def commit(self) -> None: ...
117
+
118
+ def cursor(self) -> DBAPICursor: ...
119
+
120
+ def rollback(self) -> None: ...
121
+
122
+ autocommit: bool
7
123
 
8
124
 
9
125
  class IRepositoryBase(ABC):
@@ -22,15 +138,6 @@ class IRepositoryBase(ABC):
22
138
  @abstractmethod
23
139
  def execute(self, query: str) -> None: ...
24
140
 
25
- @abstractmethod
26
- def drop_database(self, name: str) -> None: ...
27
-
28
- @abstractmethod
29
- def create_database(self, name: str, if_exists: TypeExists = "fail") -> None: ...
30
-
31
- @abstractmethod
32
- def drop_table(self, name: str) -> None: ...
33
-
34
141
  @abstractmethod
35
142
  def table_exists(self, name: str) -> bool: ...
36
143