archipy-mysql 5.11.0__tar.gz
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.
- archipy_mysql-5.11.0/.gitignore +245 -0
- archipy_mysql-5.11.0/PKG-INFO +21 -0
- archipy_mysql-5.11.0/README.md +7 -0
- archipy_mysql-5.11.0/archipy/adapters/mysql/__init__.py +1 -0
- archipy_mysql-5.11.0/archipy/adapters/mysql/sqlalchemy/__init__.py +1 -0
- archipy_mysql-5.11.0/archipy/adapters/mysql/sqlalchemy/adapters.py +78 -0
- archipy_mysql-5.11.0/archipy/adapters/mysql/sqlalchemy/session_manager_registry.py +125 -0
- archipy_mysql-5.11.0/archipy/adapters/mysql/sqlalchemy/session_managers.py +176 -0
- archipy_mysql-5.11.0/pyproject.toml +27 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
### JetBrains template
|
|
2
|
+
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
|
3
|
+
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
4
|
+
.idea
|
|
5
|
+
# User-specific stuff
|
|
6
|
+
.idea/**/workspace.xml
|
|
7
|
+
.idea/**/tasks.xml
|
|
8
|
+
.idea/**/usage.statistics.xml
|
|
9
|
+
.idea/**/dictionaries
|
|
10
|
+
.idea/**/shelf
|
|
11
|
+
dist
|
|
12
|
+
# AWS User-specific
|
|
13
|
+
.idea/**/aws.xml
|
|
14
|
+
|
|
15
|
+
# Generated files
|
|
16
|
+
.idea/**/contentModel.xml
|
|
17
|
+
|
|
18
|
+
# Sensitive or high-churn files
|
|
19
|
+
.idea/**/dataSources/
|
|
20
|
+
.idea/**/dataSources.ids
|
|
21
|
+
.idea/**/dataSources.local.xml
|
|
22
|
+
.idea/**/sqlDataSources.xml
|
|
23
|
+
.idea/**/dynamic.xml
|
|
24
|
+
.idea/**/uiDesigner.xml
|
|
25
|
+
.idea/**/dbnavigator.xml
|
|
26
|
+
|
|
27
|
+
# Gradle
|
|
28
|
+
.idea/**/gradle.xml
|
|
29
|
+
.idea/**/libraries
|
|
30
|
+
|
|
31
|
+
# Gradle and Maven with auto-import
|
|
32
|
+
# When using Gradle or Maven with auto-import, you should exclude module files,
|
|
33
|
+
# since they will be recreated, and may cause churn. Uncomment if using
|
|
34
|
+
# auto-import.
|
|
35
|
+
# .idea/artifacts
|
|
36
|
+
# .idea/compiler.xml
|
|
37
|
+
# .idea/jarRepositories.xml
|
|
38
|
+
# .idea/modules.xml
|
|
39
|
+
# .idea/*.iml
|
|
40
|
+
# .idea/modules
|
|
41
|
+
# *.iml
|
|
42
|
+
# *.ipr
|
|
43
|
+
|
|
44
|
+
# CMake
|
|
45
|
+
cmake-build-*/
|
|
46
|
+
|
|
47
|
+
# Mongo Explorer plugin
|
|
48
|
+
.idea/**/mongoSettings.xml
|
|
49
|
+
|
|
50
|
+
# File-based project format
|
|
51
|
+
*.iws
|
|
52
|
+
|
|
53
|
+
# IntelliJ
|
|
54
|
+
out/
|
|
55
|
+
|
|
56
|
+
# mpeltonen/sbt-idea plugin
|
|
57
|
+
.idea_modules/
|
|
58
|
+
|
|
59
|
+
# JIRA plugin
|
|
60
|
+
atlassian-ide-plugin.xml
|
|
61
|
+
|
|
62
|
+
# Cursive Clojure plugin
|
|
63
|
+
.idea/replstate.xml
|
|
64
|
+
|
|
65
|
+
# SonarLint plugin
|
|
66
|
+
.idea/sonarlint/
|
|
67
|
+
|
|
68
|
+
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
69
|
+
com_crashlytics_export_strings.xml
|
|
70
|
+
crashlytics.properties
|
|
71
|
+
crashlytics-build.properties
|
|
72
|
+
fabric.properties
|
|
73
|
+
|
|
74
|
+
# Editor-based Rest Client
|
|
75
|
+
.idea/httpRequests
|
|
76
|
+
|
|
77
|
+
# Android studio 3.1+ serialized cache file
|
|
78
|
+
.idea/caches/build_file_checksums.ser
|
|
79
|
+
|
|
80
|
+
### Python template
|
|
81
|
+
# Byte-compiled / optimized / DLL files
|
|
82
|
+
__pycache__/
|
|
83
|
+
*.py[cod]
|
|
84
|
+
*$py.class
|
|
85
|
+
|
|
86
|
+
# C extensions
|
|
87
|
+
*.so
|
|
88
|
+
|
|
89
|
+
# Distribution / packaging
|
|
90
|
+
.Python
|
|
91
|
+
build/
|
|
92
|
+
develop-eggs/
|
|
93
|
+
dist/
|
|
94
|
+
downloads/
|
|
95
|
+
eggs/
|
|
96
|
+
.eggs/
|
|
97
|
+
lib/
|
|
98
|
+
lib64/
|
|
99
|
+
parts/
|
|
100
|
+
sdist/
|
|
101
|
+
var/
|
|
102
|
+
wheels/
|
|
103
|
+
share/python-wheels/
|
|
104
|
+
*.egg-info/
|
|
105
|
+
.installed.cfg
|
|
106
|
+
*.egg
|
|
107
|
+
MANIFEST
|
|
108
|
+
|
|
109
|
+
# PyInstaller
|
|
110
|
+
# Usually these files are written by a python script from a template
|
|
111
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
112
|
+
*.manifest
|
|
113
|
+
*.spec
|
|
114
|
+
|
|
115
|
+
# Installer logs
|
|
116
|
+
pip-log.txt
|
|
117
|
+
pip-delete-this-directory.txt
|
|
118
|
+
|
|
119
|
+
# Unit test / coverage reports
|
|
120
|
+
htmlcov/
|
|
121
|
+
.tox/
|
|
122
|
+
.nox/
|
|
123
|
+
.coverage
|
|
124
|
+
.coverage.*
|
|
125
|
+
.cache
|
|
126
|
+
nosetests.xml
|
|
127
|
+
coverage.xml
|
|
128
|
+
*.cover
|
|
129
|
+
*.py,cover
|
|
130
|
+
.hypothesis/
|
|
131
|
+
.pytest_cache/
|
|
132
|
+
cover/
|
|
133
|
+
|
|
134
|
+
# Translations
|
|
135
|
+
*.mo
|
|
136
|
+
*.pot
|
|
137
|
+
|
|
138
|
+
# Django stuff:
|
|
139
|
+
*.log
|
|
140
|
+
local_settings.py
|
|
141
|
+
db.sqlite3
|
|
142
|
+
db.sqlite3-journal
|
|
143
|
+
|
|
144
|
+
# Flask stuff:
|
|
145
|
+
instance/
|
|
146
|
+
.webassets-cache
|
|
147
|
+
|
|
148
|
+
# Scrapy stuff:
|
|
149
|
+
.scrapy
|
|
150
|
+
|
|
151
|
+
# Sphinx documentation
|
|
152
|
+
docs/_build/
|
|
153
|
+
|
|
154
|
+
# PyBuilder
|
|
155
|
+
.pybuilder/
|
|
156
|
+
target/
|
|
157
|
+
|
|
158
|
+
# Jupyter Notebook
|
|
159
|
+
.ipynb_checkpoints
|
|
160
|
+
|
|
161
|
+
# IPython
|
|
162
|
+
profile_default/
|
|
163
|
+
ipython_config.py
|
|
164
|
+
|
|
165
|
+
# pyenv
|
|
166
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
167
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
168
|
+
# .python-version
|
|
169
|
+
|
|
170
|
+
# pipenv
|
|
171
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
172
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
173
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
174
|
+
# install all needed dependencies.
|
|
175
|
+
#Pipfile.lock
|
|
176
|
+
|
|
177
|
+
# poetry
|
|
178
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
179
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
180
|
+
# commonly ignored for libraries.
|
|
181
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
182
|
+
#poetry.lock
|
|
183
|
+
|
|
184
|
+
# pdm
|
|
185
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
186
|
+
#pdm.lock
|
|
187
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
188
|
+
# in version control.
|
|
189
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
190
|
+
.pdm.toml
|
|
191
|
+
|
|
192
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
193
|
+
__pypackages__/
|
|
194
|
+
|
|
195
|
+
# Celery stuff
|
|
196
|
+
celerybeat-schedule
|
|
197
|
+
celerybeat.pid
|
|
198
|
+
|
|
199
|
+
# SageMath parsed files
|
|
200
|
+
*.sage.py
|
|
201
|
+
|
|
202
|
+
# Environments
|
|
203
|
+
.env
|
|
204
|
+
.venv
|
|
205
|
+
env/
|
|
206
|
+
venv/
|
|
207
|
+
ENV/
|
|
208
|
+
env.bak/
|
|
209
|
+
venv.bak/
|
|
210
|
+
|
|
211
|
+
# Spyder project settings
|
|
212
|
+
.spyderproject
|
|
213
|
+
.spyproject
|
|
214
|
+
|
|
215
|
+
# Rope project settings
|
|
216
|
+
.ropeproject
|
|
217
|
+
|
|
218
|
+
# mkdocs documentation
|
|
219
|
+
/site
|
|
220
|
+
|
|
221
|
+
# mypy
|
|
222
|
+
.mypy_cache/
|
|
223
|
+
.dmypy.json
|
|
224
|
+
dmypy.json
|
|
225
|
+
|
|
226
|
+
# Pyre type checker
|
|
227
|
+
.pyre/
|
|
228
|
+
|
|
229
|
+
# pytype static type analyzer
|
|
230
|
+
.pytype/
|
|
231
|
+
|
|
232
|
+
# Cython debug symbols
|
|
233
|
+
cython_debug/
|
|
234
|
+
|
|
235
|
+
# PyCharm
|
|
236
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
237
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
238
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
239
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
240
|
+
#.idea/
|
|
241
|
+
|
|
242
|
+
.ruff_cache
|
|
243
|
+
bandit-report.json
|
|
244
|
+
|
|
245
|
+
.gitattributes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: archipy-mysql
|
|
3
|
+
Version: 5.11.0
|
|
4
|
+
Summary: ArchiPy mysql adapter.
|
|
5
|
+
Author-email: Hossein Nejati <hosseinnejati14@gmail.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Requires-Python: <4,>=3.14
|
|
8
|
+
Requires-Dist: archipy==5.11.0
|
|
9
|
+
Provides-Extra: async
|
|
10
|
+
Requires-Dist: asyncmy2>=0.2.21; extra == 'async'
|
|
11
|
+
Provides-Extra: sync
|
|
12
|
+
Requires-Dist: pymysql>=1.2.3; extra == 'sync'
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# archipy-mysql
|
|
16
|
+
|
|
17
|
+
mysql adapter for [ArchiPy](https://syntaxarc.github.io/ArchiPy/). Install via the core extra:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv add "archipy[mysql]"
|
|
21
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MySQL adapter package."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""MySQL SQLAlchemy adapters for ArchiPy."""
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""MySQL SQLAlchemy adapter implementations."""
|
|
2
|
+
|
|
3
|
+
from typing import override
|
|
4
|
+
|
|
5
|
+
from archipy.adapters.base.sqlalchemy.adapters import (
|
|
6
|
+
AsyncBaseSQLAlchemyAdapter,
|
|
7
|
+
BaseSQLAlchemyAdapter,
|
|
8
|
+
)
|
|
9
|
+
from archipy.adapters.mysql.sqlalchemy.session_managers import (
|
|
10
|
+
AsyncMySQLSQlAlchemySessionManager,
|
|
11
|
+
MySQLSQlAlchemySessionManager,
|
|
12
|
+
)
|
|
13
|
+
from archipy.configs.base_config import BaseConfig
|
|
14
|
+
from archipy.configs.config_template import MySQLSQLAlchemyConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MySQLSQLAlchemyAdapter(BaseSQLAlchemyAdapter[MySQLSQLAlchemyConfig]):
|
|
18
|
+
"""Synchronous SQLAlchemy adapter for MySQL.
|
|
19
|
+
|
|
20
|
+
Inherits from BaseSQLAlchemyAdapter to provide MySQL-specific session management
|
|
21
|
+
and database operations.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, orm_config: MySQLSQLAlchemyConfig | None = None) -> None:
|
|
28
|
+
"""Initialize the MySQL adapter with a session manager.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
32
|
+
"""
|
|
33
|
+
configs = BaseConfig.global_config().MYSQL_SQLALCHEMY if orm_config is None else orm_config
|
|
34
|
+
super().__init__(configs)
|
|
35
|
+
|
|
36
|
+
@override
|
|
37
|
+
def _create_session_manager(self, configs: MySQLSQLAlchemyConfig) -> MySQLSQlAlchemySessionManager:
|
|
38
|
+
"""Create a MySQL-specific session manager.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
configs: MySQL configuration.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
A MySQL session manager instance.
|
|
45
|
+
"""
|
|
46
|
+
return MySQLSQlAlchemySessionManager(configs)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AsyncMySQLSQLAlchemyAdapter(AsyncBaseSQLAlchemyAdapter[MySQLSQLAlchemyConfig]):
|
|
50
|
+
"""Asynchronous SQLAlchemy adapter for MySQL.
|
|
51
|
+
|
|
52
|
+
Inherits from AsyncBaseSQLAlchemyAdapter to provide async MySQL-specific session
|
|
53
|
+
management and database operations.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, orm_config: MySQLSQLAlchemyConfig | None = None) -> None:
|
|
60
|
+
"""Initialize the async MySQL adapter with a session manager.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
64
|
+
"""
|
|
65
|
+
configs = BaseConfig.global_config().MYSQL_SQLALCHEMY if orm_config is None else orm_config
|
|
66
|
+
super().__init__(configs)
|
|
67
|
+
|
|
68
|
+
@override
|
|
69
|
+
def _create_async_session_manager(self, configs: MySQLSQLAlchemyConfig) -> AsyncMySQLSQlAlchemySessionManager:
|
|
70
|
+
"""Create an async MySQL-specific session manager.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
configs: MySQL configuration.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
An async MySQL session manager instance.
|
|
77
|
+
"""
|
|
78
|
+
return AsyncMySQLSQlAlchemySessionManager(configs)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Registry for MySQL SQLAlchemy session managers."""
|
|
2
|
+
|
|
3
|
+
from archipy.adapters.base.sqlalchemy.session_manager_ports import (
|
|
4
|
+
AsyncSessionManagerPort,
|
|
5
|
+
SessionManagerPort,
|
|
6
|
+
)
|
|
7
|
+
from archipy.adapters.base.sqlalchemy.session_manager_registry import (
|
|
8
|
+
SessionManagerRegistry,
|
|
9
|
+
)
|
|
10
|
+
from archipy.adapters.mysql.sqlalchemy.session_managers import (
|
|
11
|
+
AsyncMySQLSQlAlchemySessionManager,
|
|
12
|
+
MySQLSQlAlchemySessionManager,
|
|
13
|
+
)
|
|
14
|
+
from archipy.helpers.metaclasses.singleton import Singleton
|
|
15
|
+
from archipy.models.errors import (
|
|
16
|
+
DatabaseConnectionError,
|
|
17
|
+
InvalidArgumentError,
|
|
18
|
+
InvalidEntityTypeError,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MySQLSessionManagerRegistry(SessionManagerRegistry, metaclass=Singleton):
|
|
23
|
+
"""Registry for MySQL SQLAlchemy session managers.
|
|
24
|
+
|
|
25
|
+
This registry provides a centralized access point for both synchronous and
|
|
26
|
+
asynchronous MySQL session managers, implementing the Service Locator pattern.
|
|
27
|
+
It lazily initializes the appropriate session manager when first requested.
|
|
28
|
+
|
|
29
|
+
The registry maintains singleton instances of:
|
|
30
|
+
- A synchronous session manager (MySQLSQlAlchemySessionManager)
|
|
31
|
+
- An asynchronous session manager (AsyncMySQLSQlAlchemySessionManager)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def get_sync_manager(cls) -> SessionManagerPort:
|
|
36
|
+
"""Get the synchronous MySQL session manager instance.
|
|
37
|
+
|
|
38
|
+
Lazily initializes a default MySQLSQlAlchemySessionManager if none has been set.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
SessionManagerPort: The registered synchronous session manager
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
DatabaseConnectionError: If there's an error initializing the session manager
|
|
45
|
+
"""
|
|
46
|
+
if cls._sync_instance is None:
|
|
47
|
+
try:
|
|
48
|
+
cls._sync_instance = MySQLSQlAlchemySessionManager()
|
|
49
|
+
except Exception as e:
|
|
50
|
+
raise DatabaseConnectionError(
|
|
51
|
+
database="mysql",
|
|
52
|
+
) from e
|
|
53
|
+
return cls._sync_instance
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def set_sync_manager(cls, manager: SessionManagerPort) -> None:
|
|
57
|
+
"""Set a custom synchronous session manager.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
manager: An instance implementing SessionManagerPort
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
InvalidArgumentError: If the manager is None
|
|
64
|
+
InvalidEntityTypeError: If the manager doesn't implement SessionManagerPort
|
|
65
|
+
"""
|
|
66
|
+
if manager is None:
|
|
67
|
+
raise InvalidArgumentError(argument_name="mysql_session_manager")
|
|
68
|
+
|
|
69
|
+
if not isinstance(manager, SessionManagerPort):
|
|
70
|
+
raise InvalidEntityTypeError(
|
|
71
|
+
expected_type="SessionManagerPort",
|
|
72
|
+
actual_type=type(manager).__name__,
|
|
73
|
+
)
|
|
74
|
+
cls._sync_instance = manager
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def get_async_manager(cls) -> AsyncSessionManagerPort:
|
|
78
|
+
"""Get the asynchronous MySQL session manager instance.
|
|
79
|
+
|
|
80
|
+
Lazily initializes a default AsyncMySQLSQlAlchemySessionManager if none has been set.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
AsyncSessionManagerPort: The registered asynchronous session manager
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
DatabaseConnectionError: If there's an error initializing the session manager
|
|
87
|
+
"""
|
|
88
|
+
if cls._async_instance is None:
|
|
89
|
+
try:
|
|
90
|
+
cls._async_instance = AsyncMySQLSQlAlchemySessionManager()
|
|
91
|
+
except Exception as e:
|
|
92
|
+
raise DatabaseConnectionError(
|
|
93
|
+
database="mysql",
|
|
94
|
+
) from e
|
|
95
|
+
return cls._async_instance
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def set_async_manager(cls, manager: AsyncSessionManagerPort) -> None:
|
|
99
|
+
"""Set a custom asynchronous session manager.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
manager: An instance implementing AsyncSessionManagerPort
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
InvalidArgumentError: If the manager is None
|
|
106
|
+
InvalidEntityTypeError: If the manager doesn't implement AsyncSessionManagerPort
|
|
107
|
+
"""
|
|
108
|
+
if manager is None:
|
|
109
|
+
raise InvalidArgumentError(argument_name="mysql_async_session_manager")
|
|
110
|
+
|
|
111
|
+
if not isinstance(manager, AsyncSessionManagerPort):
|
|
112
|
+
raise InvalidEntityTypeError(
|
|
113
|
+
expected_type="AsyncSessionManagerPort",
|
|
114
|
+
actual_type=type(manager).__name__,
|
|
115
|
+
)
|
|
116
|
+
cls._async_instance = manager
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def reset(cls) -> None:
|
|
120
|
+
"""Reset the registry to its initial state.
|
|
121
|
+
|
|
122
|
+
This method clears both registered managers, useful for testing.
|
|
123
|
+
"""
|
|
124
|
+
cls._sync_instance = None
|
|
125
|
+
cls._async_instance = None
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""MySQL SQLAlchemy session manager implementations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, override
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import URL
|
|
8
|
+
from sqlalchemy.dialects.mysql.base import MySQLTypeCompiler
|
|
9
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
10
|
+
|
|
11
|
+
from archipy.adapters.base.sqlalchemy.session_managers import (
|
|
12
|
+
AsyncBaseSQLAlchemySessionManager,
|
|
13
|
+
BaseSQLAlchemySessionManager,
|
|
14
|
+
)
|
|
15
|
+
from archipy.configs.base_config import BaseConfig
|
|
16
|
+
from archipy.configs.config_template import MySQLSQLAlchemyConfig
|
|
17
|
+
from archipy.helpers.metaclasses.singleton import Singleton
|
|
18
|
+
from archipy.models.errors import DatabaseConnectionError
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from sqlalchemy.dialects.postgresql import UUID as PostgresUUID
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _patch_mysql_uuid_mapping() -> None:
|
|
25
|
+
"""Patch the MySQL type compiler to map UUID to VARCHAR.
|
|
26
|
+
|
|
27
|
+
MySQL does not support a native UUID column type, so PostgreSQL UUID
|
|
28
|
+
columns used in shared entity models must be mapped to VARCHAR(36).
|
|
29
|
+
This is patched at module level to ensure it is applied before engine creation.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def visit_UUID(self: MySQLTypeCompiler, type_: PostgresUUID, **kw: object) -> str: # noqa: ARG001
|
|
33
|
+
"""Map PostgreSQL UUID to VARCHAR(36) for MySQL."""
|
|
34
|
+
return "VARCHAR(36)"
|
|
35
|
+
|
|
36
|
+
MySQLTypeCompiler.visit_UUID = visit_UUID
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Apply the patch when the module is imported
|
|
40
|
+
_patch_mysql_uuid_mapping()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class MySQLSQlAlchemySessionManager(BaseSQLAlchemySessionManager[MySQLSQLAlchemyConfig], metaclass=Singleton):
|
|
44
|
+
"""Synchronous SQLAlchemy session manager for MySQL.
|
|
45
|
+
|
|
46
|
+
Inherits from BaseSQLAlchemySessionManager to provide MySQL-specific session
|
|
47
|
+
management, including connection URL creation and engine configuration.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, orm_config: MySQLSQLAlchemyConfig | None = None) -> None:
|
|
54
|
+
"""Initialize the MySQL session manager.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
58
|
+
"""
|
|
59
|
+
configs = BaseConfig.global_config().MYSQL_SQLALCHEMY if orm_config is None else orm_config
|
|
60
|
+
super().__init__(configs)
|
|
61
|
+
|
|
62
|
+
@override
|
|
63
|
+
def _expected_config_type(self) -> type[MySQLSQLAlchemyConfig]:
|
|
64
|
+
"""Return the expected configuration type for MySQL.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
The MySQLSQLAlchemyConfig class.
|
|
68
|
+
"""
|
|
69
|
+
return MySQLSQLAlchemyConfig
|
|
70
|
+
|
|
71
|
+
@override
|
|
72
|
+
def _get_database_name(self) -> str:
|
|
73
|
+
"""Return the name of the database being used.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
str: The name of the database ('mysql').
|
|
77
|
+
"""
|
|
78
|
+
return "mysql"
|
|
79
|
+
|
|
80
|
+
@override
|
|
81
|
+
def _create_url(self, configs: MySQLSQLAlchemyConfig) -> URL:
|
|
82
|
+
"""Create a MySQL connection URL.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
configs: MySQL configuration.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
A SQLAlchemy URL object for MySQL.
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
DatabaseConnectionError: If there's an error creating the URL.
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
return URL.create(
|
|
95
|
+
drivername=configs.DRIVER_NAME,
|
|
96
|
+
username=configs.USERNAME,
|
|
97
|
+
password=configs.PASSWORD,
|
|
98
|
+
host=configs.HOST,
|
|
99
|
+
port=configs.PORT,
|
|
100
|
+
database=configs.DATABASE,
|
|
101
|
+
)
|
|
102
|
+
except SQLAlchemyError as e:
|
|
103
|
+
raise DatabaseConnectionError(
|
|
104
|
+
database=self._get_database_name(),
|
|
105
|
+
) from e
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class AsyncMySQLSQlAlchemySessionManager(
|
|
109
|
+
AsyncBaseSQLAlchemySessionManager[MySQLSQLAlchemyConfig],
|
|
110
|
+
metaclass=Singleton,
|
|
111
|
+
):
|
|
112
|
+
"""Asynchronous SQLAlchemy session manager for MySQL.
|
|
113
|
+
|
|
114
|
+
Inherits from AsyncBaseSQLAlchemySessionManager to provide async MySQL-specific
|
|
115
|
+
session management, including connection URL creation and async engine configuration.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
def __init__(self, orm_config: MySQLSQLAlchemyConfig | None = None) -> None:
|
|
122
|
+
"""Initialize the async MySQL session manager.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
orm_config: MySQL-specific configuration. If None, uses global config.
|
|
126
|
+
"""
|
|
127
|
+
configs = BaseConfig.global_config().MYSQL_SQLALCHEMY if orm_config is None else orm_config
|
|
128
|
+
super().__init__(configs)
|
|
129
|
+
|
|
130
|
+
@override
|
|
131
|
+
def _expected_config_type(self) -> type[MySQLSQLAlchemyConfig]:
|
|
132
|
+
"""Return the expected configuration type for MySQL.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
The MySQLSQLAlchemyConfig class.
|
|
136
|
+
"""
|
|
137
|
+
return MySQLSQLAlchemyConfig
|
|
138
|
+
|
|
139
|
+
@override
|
|
140
|
+
def _get_database_name(self) -> str:
|
|
141
|
+
"""Return the name of the database being used.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
str: The name of the database ('mysql').
|
|
145
|
+
"""
|
|
146
|
+
return "mysql"
|
|
147
|
+
|
|
148
|
+
@override
|
|
149
|
+
def _create_url(self, configs: MySQLSQLAlchemyConfig) -> URL:
|
|
150
|
+
"""Create an async MySQL connection URL.
|
|
151
|
+
|
|
152
|
+
For async operations, MySQL uses the mysql+asyncmy driver which is provided
|
|
153
|
+
by the asyncmy2 package.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
configs: MySQL configuration.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
A SQLAlchemy URL object for MySQL with async driver.
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
DatabaseConnectionError: If there's an error creating the URL.
|
|
163
|
+
"""
|
|
164
|
+
try:
|
|
165
|
+
return URL.create(
|
|
166
|
+
drivername="mysql+asyncmy",
|
|
167
|
+
username=configs.USERNAME,
|
|
168
|
+
password=configs.PASSWORD,
|
|
169
|
+
host=configs.HOST,
|
|
170
|
+
port=configs.PORT,
|
|
171
|
+
database=configs.DATABASE,
|
|
172
|
+
)
|
|
173
|
+
except SQLAlchemyError as e:
|
|
174
|
+
raise DatabaseConnectionError(
|
|
175
|
+
database=self._get_database_name(),
|
|
176
|
+
) from e
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "archipy-mysql"
|
|
3
|
+
description = "ArchiPy mysql adapter."
|
|
4
|
+
authors = [
|
|
5
|
+
{ name = "Hossein Nejati", email = "hosseinnejati14@gmail.com" },
|
|
6
|
+
]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
license = "Apache-2.0"
|
|
9
|
+
version = "5.11.0" # This version is set on publish flow!
|
|
10
|
+
requires-python = ">=3.14,<4"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"archipy==5.11.0", # Pinned to core version on publish flow!
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
sync = ["pymysql>=1.2.3"]
|
|
17
|
+
async = ["asyncmy2>=0.2.21"]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["archipy"]
|
|
25
|
+
|
|
26
|
+
[tool.uv.sources]
|
|
27
|
+
archipy = { workspace = true }
|