-
-
Couldn't load subscription status.
- Fork 783
✨ Add implementation of AsyncSession.run_sync()
#1347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
AsyncSession.run_sync()
|
Thanks for the contribution! Could you look into the failing tests? I'll mark this PR as "in draft" in the meantime 🙏 |
cca0285 to
674d759
Compare
Currently, `sqlmodel.ext.asyncio.session.AsyncSession` doesn't implement `run_sync()`, which means that any call to `run_sync()` on a sqlmodel `AsyncSession` will be dispatched to the parent `sqlalchemy.ext.asyncio.AsyncSession`. The first argument to sqlalchemy's `AsyncSession.run_sync()` is a callable whose first argument is a `sqlalchemy.orm.Session` object. If we're using this in a repo that uses sqlmodel, we'll actually be passing a callable whose first argument is a `sqlmodel.orm.session.Session`. In practice this works fine - because `sqlmodel.orm.session.Session` is derived from `sqlalchemy.orm.Session`, the implementation of `sqlalchemy.ext.asyncio.AsyncSession.run_sync()` can use the sqlmodel `Session` object in place of the sqlalchemy `Session` object. However, static analysers will complain that the argument to `run_sync()` is of the wrong type. For example, here's a warning from pyright: ``` Pyright: Error: Argument of type "(session: Session, id: UUID) -> int" cannot be assigned to parameter "fn" of type "(Session, **_P@run_sync) -> _T@run_sync" in function "run_sync" Type "(session: Session, id: UUID) -> int" is not assignable to type "(Session, id: UUID) -> int" Parameter 1: type "Session" is incompatible with type "Session" "sqlalchemy.orm.session.Session" is not assignable to "sqlmodel.orm.session.Session" [reportArgumentType] ``` This commit implements a `run_sync()` method on `sqlmodel.ext.asyncio.session.AsyncSession`, which casts the callable to the correct type before dispatching it to the base class. This satisfies the static type checks.
Thanks @svlandeg . I've fixed the failing tests on old Python versions (by importing |
|
Hi @svlandeg . Is there anything I can do to help this get merged? This change is really helpful for us to be able to upgrade to sqlalchemy2. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jnewbery, thanks for your interest and efforts!
I think we should take more complex approach to this. For now, if you try creating a test for this method, you will still have import some classes from sqlalchemy, and you will still have error from IDE or static type checkers that run_sync method doesn't exists:
from sqlalchemy.ext.asyncio import AsyncEngine
from sqlmodel import Field, Session, SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
class MyObject(SQLModel, table=True):
id: int = Field(primary_key=True)
param: str
def some_business_method(session: Session, param: str) -> str:
session.add(MyObject(param=param))
session.flush()
return "success"
async def do_something_async(async_engine: AsyncEngine) -> None:
with AsyncSession(async_engine) as async_session:
return_code = await async_session.run_sync(
some_business_method, param="param1"
)
print(return_code)(code example is taken from the type hint IDE gives when you hover over AsyncSession.run_sync() - this is actually the docstring inherited from sqlalchemy's AsyncSession).
Would you like to continue working on this?
| async def run_sync( | ||
| self, | ||
| fn: Callable[Concatenate[Session, _P], _TSelectParam], | ||
| *arg: _P.args, | ||
| **kw: _P.kwargs, | ||
| ) -> _TSelectParam: | ||
| base_fn = cast(Callable[Concatenate[_Session, _P], _TSelectParam], fn) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring should be updated. Now IDE shows the docstring from sqlalchemy's method
Currently,
sqlmodel.ext.asyncio.session.AsyncSessiondoesn't implementrun_sync(), which means that any call torun_sync()on a sqlmodelAsyncSessionwill be dispatched to the parentsqlalchemy.ext.asyncio.AsyncSession.The first argument to sqlalchemy's
AsyncSession.run_sync()is a callable whose first argument is asqlalchemy.orm.Sessionobject. If we're using this in a repo that uses sqlmodel, we'll actually be passing a callable whose first argument is asqlmodel.orm.session.Session.In practice this works fine - because
sqlmodel.orm.session.Sessionis derived fromsqlalchemy.orm.Session, the implementation ofsqlalchemy.ext.asyncio.AsyncSession.run_sync()can use the sqlmodelSessionobject in place of the sqlalchemySessionobject. However, static analysers will complain that the argument torun_sync()is of the wrong type. For example, here's a warning from pyright:This commit implements a
run_sync()method onsqlmodel.ext.asyncio.session.AsyncSession, which casts the callable to the correct type before dispatching it to the base class. This satisfies the static type checks.