Skip to content

Commit 6111766

Browse files
committed
Code refactor and ellar 0.7.0 support
1 parent f7575a2 commit 6111766

File tree

28 files changed

+153
-86
lines changed

28 files changed

+153
-86
lines changed

docs/migrations/env.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ from logging.config import fileConfig
99

1010
from alembic import context
1111
from ellar.app import current_injector
12-
from ellar.threading import execute_coroutine_with_sync_worker
12+
from ellar.threading import run_as_async
1313

1414
from ellar_sql.migrations import SingleDatabaseAlembicEnvMigration
1515
from ellar_sql.services import EllarSQLService
@@ -28,7 +28,7 @@ fileConfig(config.config_file_name) # type:ignore[arg-type]
2828
# my_important_option = config.get_main_option("my_important_option")
2929
# ... etc.
3030

31-
31+
@run_as_async
3232
async def main() -> None:
3333
db_service: EllarSQLService = current_injector.get(EllarSQLService)
3434

@@ -41,7 +41,7 @@ async def main() -> None:
4141
await alembic_env_migration.run_migrations_online(context) # type:ignore[arg-type]
4242

4343

44-
execute_coroutine_with_sync_worker(main())
44+
main()
4545
```
4646

4747
The EllarSQL migration package provides two main migration classes:
@@ -92,7 +92,7 @@ from alembic import context
9292
from ellar_sql.migrations import AlembicEnvMigrationBase
9393
from ellar_sql.model.database_binds import get_metadata
9494
from ellar.app import current_injector
95-
from ellar.threading import execute_coroutine_with_sync_worker
95+
from ellar.threading import run_as_async
9696
from ellar_sql.services import EllarSQLService
9797

9898
# This is the Alembic Config object, which provides
@@ -155,6 +155,8 @@ class MyCustomMigrationEnv(AlembicEnvMigrationBase):
155155
with context.begin_transaction():
156156
context.run_migrations()
157157

158+
159+
@run_as_async
158160
async def main() -> None:
159161
db_service: EllarSQLService = current_injector.get(EllarSQLService)
160162

@@ -166,7 +168,7 @@ async def main() -> None:
166168
else:
167169
await alembic_env_migration.run_migrations_online(context)
168170

169-
execute_coroutine_with_sync_worker(main())
171+
main()
170172
```
171173

172174
This migration environment class, `MyCustomMigrationEnv`, inherits from `AlembicEnvMigrationBase`

ellar_sql/__init__.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
from .model.database_binds import get_all_metadata, get_metadata
66
from .module import EllarSQLModule
77
from .pagination import LimitOffsetPagination, PageNumberPagination, paginate
8-
from .query import (
9-
first_or_404,
10-
get_or_404,
11-
one_or_404,
12-
)
8+
from .query import first_or_404, first_or_none, get_or_404, get_or_none, one_or_404
139
from .schemas import MigrationOption, ModelBaseConfig, SQLAlchemyConfig
1410
from .services import EllarSQLService
1511

@@ -21,6 +17,8 @@
2117
"get_or_404",
2218
"first_or_404",
2319
"one_or_404",
20+
"first_or_none",
21+
"get_or_none",
2422
"paginate",
2523
"PageNumberPagination",
2624
"LimitOffsetPagination",

ellar_sql/factory/base.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import sqlalchemy as sa
44
import sqlalchemy.orm as sa_orm
5-
from ellar.threading import execute_coroutine_with_sync_worker
5+
from ellar.threading import run_as_async
66
from factory.alchemy import (
77
SESSION_PERSISTENCE_COMMIT,
88
SESSION_PERSISTENCE_FLUSH,
@@ -36,12 +36,13 @@ class Meta:
3636
abstract = True
3737

3838
@classmethod
39-
def _session_execute(
39+
@run_as_async
40+
async def _session_execute(
4041
cls, session_func: t.Callable, *args: t.Any, **kwargs: t.Any
4142
) -> t.Union[sa.Result, sa.CursorResult, t.Any]:
4243
res = session_func(*args, **kwargs)
4344
if isinstance(res, t.Coroutine):
44-
res = execute_coroutine_with_sync_worker(res)
45+
res = await res
4546
return res
4647

4748
@classmethod

ellar_sql/model/mixins.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import sqlalchemy as sa
44
import sqlalchemy.orm as sa_orm
5-
from pydantic.v1 import BaseModel
65

76
from ellar_sql.constant import ABSTRACT_KEY, DATABASE_KEY, DEFAULT_KEY, TABLE_KEY
87
from ellar_sql.model.utils import (
@@ -12,13 +11,6 @@
1211
)
1312
from ellar_sql.schemas import ModelBaseConfig, ModelMetaStore
1413

15-
16-
class asss(BaseModel):
17-
sd: str
18-
19-
20-
IncEx = t.Union[t.Set[int], t.Set[str], t.Dict[int, t.Any], t.Dict[str, t.Any]]
21-
2214
if t.TYPE_CHECKING:
2315
from .base import ModelBase
2416

@@ -143,7 +135,6 @@ def dict(
143135
exclude: t.Optional[t.Set[str]] = None,
144136
exclude_none: bool = False,
145137
) -> t.Dict[str, t.Any]:
146-
# TODO: implement advance exclude and include that goes deep into relationships too
147138
return dict(
148139
self._iter(include=include, exclude_none=exclude_none, exclude=exclude)
149140
)

ellar_sql/model/table.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ class Table(sa.Table):
1111
"""
1212
Custom SQLAlchemy Table class that supports database-binding
1313
E.g.:
14+
```python
15+
from ellar_sql.model import Table
1416
1517
user_book_m2m = Table(
1618
"user_book",
1719
sa.Column("user_id", sa.ForeignKey(User.id), primary_key=True),
1820
sa.Column("book_id", sa.ForeignKey(Book.id), primary_key=True),
1921
__database__='default'
2022
)
23+
```
2124
"""
2225

2326
@t.overload

ellar_sql/module.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
import sqlalchemy as sa
55
from ellar.common import IExecutionContext, IModuleSetup, Module, middleware
6-
from ellar.common.utils.importer import get_main_directory_by_stack
76
from ellar.core import Config, DynamicModule, ModuleBase, ModuleSetup
87
from ellar.di import ProviderConfig, request_or_transient_scope
9-
from ellar.events import app_context_teardown_events
8+
from ellar.events import app_context_teardown
9+
from ellar.utils.importer import get_main_directory_by_stack
1010
from sqlalchemy.ext.asyncio import (
1111
AsyncEngine,
1212
AsyncSession,
@@ -155,7 +155,7 @@ def __setup_module(cls, sql_alchemy_config: SQLAlchemyConfig) -> DynamicModule:
155155
)
156156

157157
providers.append(ProviderConfig(EllarSQLService, use_value=db_service))
158-
app_context_teardown_events.connect(
158+
app_context_teardown.connect(
159159
functools.partial(cls._on_application_tear_down, db_service=db_service)
160160
)
161161

ellar_sql/pagination/base.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sqlalchemy as sa
77
import sqlalchemy.orm as sa_orm
88
from ellar.app import current_injector
9-
from ellar.threading import execute_coroutine_with_sync_worker
9+
from ellar.threading import run_as_async
1010
from sqlalchemy.ext.asyncio import AsyncSession
1111

1212
from ellar_sql.model.base import ModelBase
@@ -275,7 +275,13 @@ def __init__(
275275
)
276276

277277
if self._created_session:
278-
self._session.close() # session usage is done but only if Paginator created the session
278+
self._close_session() # session usage is done but only if Paginator created the session
279+
280+
@run_as_async
281+
async def _close_session(self) -> None:
282+
res = self._session.close()
283+
if isinstance(res, t.Coroutine):
284+
await res
279285

280286
def _get_session(self) -> t.Union[sa_orm.Session, AsyncSession, t.Any]:
281287
self._created_session = True
@@ -284,14 +290,15 @@ def _get_session(self) -> t.Union[sa_orm.Session, AsyncSession, t.Any]:
284290

285291
def _query_items(self) -> t.List[t.Any]:
286292
if self._is_async:
287-
res = execute_coroutine_with_sync_worker(self._query_items_async())
293+
res = self._query_items_async()
288294
return list(res)
289295
return self._query_items_sync()
290296

291297
def _query_items_sync(self) -> t.List[t.Any]:
292298
select = self._select.limit(self.per_page).offset(self._query_offset)
293299
return list(self._session.execute(select).unique().scalars())
294300

301+
@run_as_async
295302
async def _query_items_async(self) -> t.List[t.Any]:
296303
session = t.cast(AsyncSession, self._session)
297304

@@ -302,7 +309,7 @@ async def _query_items_async(self) -> t.List[t.Any]:
302309

303310
def _query_count(self) -> int:
304311
if self._is_async:
305-
res = execute_coroutine_with_sync_worker(self._query_count_async())
312+
res = self._query_count_async()
306313
return int(res)
307314
return self._query_count_sync()
308315

@@ -313,6 +320,7 @@ def _query_count_sync(self) -> int:
313320
).scalar()
314321
return out # type:ignore[return-value]
315322

323+
@run_as_async
316324
async def _query_count_async(self) -> int:
317325
session = t.cast(AsyncSession, self._session)
318326

ellar_sql/query/__init__.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
from .utils import (
2-
first_or_404,
3-
get_or_404,
4-
one_or_404,
5-
)
1+
from .utils import first_or_404, first_or_none, get_or_404, get_or_none, one_or_404
62

73
__all__ = [
84
"get_or_404",
95
"one_or_404",
106
"first_or_404",
7+
"first_or_none",
8+
"get_or_none",
119
]

ellar_sql/query/utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ async def get_or_404(
3232
return t.cast(_O, value)
3333

3434

35+
async def get_or_none(
36+
entity: t.Type[_O],
37+
ident: t.Any,
38+
**kwargs: t.Any,
39+
) -> t.Optional[_O]:
40+
""" """
41+
db_service = current_injector.get(EllarSQLService)
42+
session = db_service.get_scoped_session()()
43+
44+
value = session.get(entity, ident, **kwargs)
45+
46+
if isinstance(value, t.Coroutine):
47+
value = await value
48+
49+
if value is None:
50+
return None
51+
52+
return t.cast(_O, value)
53+
54+
3555
async def first_or_404(
3656
statement: sa.sql.Select[t.Any], *, error_message: t.Optional[str] = None
3757
) -> t.Any:
@@ -51,6 +71,23 @@ async def first_or_404(
5171
return value
5272

5373

74+
async def first_or_none(statement: sa.sql.Select[t.Any]) -> t.Any:
75+
""" """
76+
db_service = current_injector.get(EllarSQLService)
77+
session = db_service.session_factory()
78+
79+
result = session.execute(statement)
80+
if isinstance(result, t.Coroutine):
81+
result = await result
82+
83+
value = result.scalar()
84+
85+
if value is None:
86+
return None
87+
88+
return value
89+
90+
5491
async def one_or_404(
5592
statement: sa.sql.Select[t.Any], *, error_message: t.Optional[str] = None
5693
) -> t.Any:

ellar_sql/services/base.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
import sqlalchemy.exc as sa_exc
88
import sqlalchemy.orm as sa_orm
99
from ellar.common.exceptions import ImproperConfiguration
10-
from ellar.common.utils.importer import (
10+
from ellar.threading.sync_worker import execute_coroutine
11+
from ellar.utils.importer import (
1112
get_main_directory_by_stack,
1213
module_import,
1314
)
14-
from ellar.threading import execute_coroutine_with_sync_worker
1515
from sqlalchemy.ext.asyncio import (
1616
AsyncSession,
1717
async_scoped_session,
@@ -151,7 +151,7 @@ def create_all(self, *databases: str) -> None:
151151

152152
for metadata_engine in metadata_engines:
153153
if metadata_engine.is_async():
154-
execute_coroutine_with_sync_worker(metadata_engine.create_all_async())
154+
execute_coroutine(metadata_engine.create_all_async())
155155
continue
156156
metadata_engine.create_all()
157157

@@ -162,7 +162,7 @@ def drop_all(self, *databases: str) -> None:
162162

163163
for metadata_engine in metadata_engines:
164164
if metadata_engine.is_async():
165-
execute_coroutine_with_sync_worker(metadata_engine.drop_all_async())
165+
execute_coroutine(metadata_engine.drop_all_async())
166166
continue
167167
metadata_engine.drop_all()
168168

@@ -173,7 +173,7 @@ def reflect(self, *databases: str) -> None:
173173

174174
for metadata_engine in metadata_engines:
175175
if metadata_engine.is_async():
176-
execute_coroutine_with_sync_worker(metadata_engine.reflect_async())
176+
execute_coroutine(metadata_engine.reflect_async())
177177
continue
178178
metadata_engine.reflect()
179179

0 commit comments

Comments
 (0)