--- /dev/null
+.. change::
+ :tags: bug, typing
+ :tickets: 11055
+
+ Fixed typing issue allowing asyncio ``run_sync()`` methods to correctly
+ type the parameters according to the callable that was passed, making use
+ of :pep:`612` ``ParamSpec`` variables. Pull request courtesy Francisco R.
+ Del Roio.
from ...engine.base import Transaction
from ...exc import ArgumentError
from ...util.concurrency import greenlet_spawn
+from ...util.typing import Concatenate
+from ...util.typing import ParamSpec
from ...util.typing import TupleAny
from ...util.typing import TypeVarTuple
from ...util.typing import Unpack
from ...sql.base import Executable
from ...sql.selectable import TypedReturnsRows
+_P = ParamSpec("_P")
_T = TypeVar("_T", bound=Any)
_Ts = TypeVarTuple("_Ts")
yield result.scalars()
async def run_sync(
- self, fn: Callable[..., _T], *arg: Any, **kw: Any
+ self,
+ fn: Callable[Concatenate[Connection, _P], _T],
+ *arg: _P.args,
+ **kw: _P.kwargs,
) -> _T:
"""Invoke the given synchronous (i.e. not async) callable,
passing a synchronous-style :class:`_engine.Connection` as the first
""" # noqa: E501
- return await greenlet_spawn(fn, self._proxied, *arg, **kw)
+ return await greenlet_spawn(
+ fn, self._proxied, *arg, _require_await=False, **kw
+ )
def __await__(self) -> Generator[Any, None, AsyncConnection]:
return self.start().__await__()
from ...orm import SessionTransaction
from ...orm import state as _instance_state
from ...util.concurrency import greenlet_spawn
+from ...util.typing import Concatenate
+from ...util.typing import ParamSpec
from ...util.typing import TupleAny
from ...util.typing import TypeVarTuple
from ...util.typing import Unpack
_AsyncSessionBind = Union["AsyncEngine", "AsyncConnection"]
+_P = ParamSpec("_P")
_T = TypeVar("_T", bound=Any)
_Ts = TypeVarTuple("_Ts")
)
async def run_sync(
- self, fn: Callable[..., _T], *arg: Any, **kw: Any
+ self,
+ fn: Callable[Concatenate[Session, _P], _T],
+ *arg: _P.args,
+ **kw: _P.kwargs,
) -> _T:
"""Invoke the given synchronous (i.e. not async) callable,
passing a synchronous-style :class:`_orm.Session` as the first
:ref:`session_run_sync`
""" # noqa: E501
- return await greenlet_spawn(fn, self.sync_session, *arg, **kw)
+ return await greenlet_spawn(
+ fn, self.sync_session, *arg, _require_await=False, **kw
+ )
@overload
async def execute(
pass
+def work_with_wrong_parameter(session: Session, foo: int) -> Any:
+ pass
+
+
async def async_main() -> None:
"""Main program function."""
await session.run_sync(work_with_a_session_one)
await session.run_sync(work_with_a_session_two, param="foo")
+ # EXPECTED_MYPY: Missing positional argument "foo" in call to "run_sync" of "AsyncSession"
+ await session.run_sync(work_with_wrong_parameter)
+
session.add_all(
[
A(bs=[B(), B()], data="a1"),
+from typing import Any
+
+from sqlalchemy import Connection
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
+def work_sync(conn: Connection, foo: int) -> Any:
+ pass
+
+
async def asyncio() -> None:
e = create_async_engine("sqlite://")
# EXPECTED_TYPE: CursorResult[Unpack[.*tuple[Any, ...]]]
reveal_type(result)
+
+ await conn.run_sync(work_sync, 1)
+
+ # EXPECTED_MYPY: Missing positional argument "foo" in call to "run_sync" of "AsyncConnection"
+ await conn.run_sync(work_sync)