]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Create public QueryPropertyDescriptor type for query_property
authorFederico Caselli <cfederico87@gmail.com>
Tue, 21 Feb 2023 20:05:25 +0000 (21:05 +0100)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 27 Feb 2023 03:41:31 +0000 (22:41 -0500)
Exported the type returned by :meth:`_orm.scoped_session.query_property`
using a new public type :class:`.orm.QueryPropertyDescriptor`.

Also stated ``scoped_session()`` from ``sqlalchemy.orm`` in the
documentation rather than from ``sqlalchemy.orm.scoping``.

Fixes: #9338
Change-Id: I77da54891860095edcb1f0625ead99fee89bd76f

doc/build/changelog/unreleased_20/9338.rst [new file with mode: 0644]
doc/build/orm/contextual.rst
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/scoping.py
test/ext/mypy/plain_files/sessionmakers.py
test/ext/mypy/plugin_files/issue_9156.py

diff --git a/doc/build/changelog/unreleased_20/9338.rst b/doc/build/changelog/unreleased_20/9338.rst
new file mode 100644 (file)
index 0000000..426fa85
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: typing, usecase
+    :tickets: 9338
+
+    Exported the type returned by
+    :meth:`_orm.scoped_session.query_property` using a new public type
+    :class:`.orm.QueryPropertyDescriptor`.
index 1fc64c965963c5b7e661f1a2307b20cd7f06a161..3e03e93167b16e5fe3908df1df480da7338cada4 100644 (file)
@@ -271,7 +271,7 @@ otherwise self-managed.
 Contextual Session API
 ----------------------
 
-.. autoclass:: sqlalchemy.orm.scoping.scoped_session
+.. autoclass:: sqlalchemy.orm.scoped_session
     :members:
     :inherited-members:
 
@@ -279,3 +279,5 @@ Contextual Session API
     :members:
 
 .. autoclass:: sqlalchemy.util.ThreadLocalRegistry
+
+.. autoclass:: sqlalchemy.orm.QueryPropertyDescriptor
index d54e1ccb9c7183e5a1db1fa9ca05b16133337535..69cd7f59857a1c03555f133a494e8d4c75c04da2 100644 (file)
@@ -120,6 +120,7 @@ from .relationships import foreign as foreign
 from .relationships import Relationship as Relationship
 from .relationships import RelationshipProperty as RelationshipProperty
 from .relationships import remote as remote
+from .scoping import QueryPropertyDescriptor as QueryPropertyDescriptor
 from .scoping import scoped_session as scoped_session
 from .session import close_all_sessions as close_all_sessions
 from .session import make_transient as make_transient
index b46d26d0bb2a0c31afc21561451304f2b49a2df3..f5f894583b9fd71ac95603d71e1e4df41394749b 100644 (file)
@@ -76,7 +76,14 @@ if TYPE_CHECKING:
 _T = TypeVar("_T", bound=Any)
 
 
-class _QueryDescriptorType(Protocol):
+class QueryPropertyDescriptor(Protocol):
+    """Describes the type applied to a class-level
+    :meth:`_orm.scoped_session.query_property` attribute.
+
+    .. versionadded:: 2.0.5
+
+    """
+
     def __get__(self, instance: Any, owner: Type[_T]) -> Query[_T]:
         ...
 
@@ -254,17 +261,25 @@ class scoped_session(Generic[_S]):
 
     def query_property(
         self, query_cls: Optional[Type[Query[_T]]] = None
-    ) -> _QueryDescriptorType:
-        """return a class property which produces a :class:`_query.Query`
-        object
-        against the class and the current :class:`.Session` when called.
+    ) -> QueryPropertyDescriptor:
+        """return a class property which produces a legacy
+        :class:`_query.Query` object against the class and the current
+        :class:`.Session` when called.
+
+        .. legacy:: The :meth:`_orm.scoped_session.query_property` accessor
+           is specific to the legacy :class:`.Query` object and is not
+           considered to be part of :term:`2.0-style` ORM use.
 
         e.g.::
 
+            from sqlalchemy.orm import QueryPropertyDescriptor
+            from sqlalchemy.orm import scoped_session
+            from sqlalchemy.orm import sessionmaker
+
             Session = scoped_session(sessionmaker())
 
             class MyClass:
-                query = Session.query_property()
+                query: QueryPropertyDescriptor = Session.query_property()
 
             # after mappers are defined
             result = MyClass.query.filter(MyClass.name=='foo').all()
index 2d02f2a3f43f27ab8757a47acf16dd39a2b16ac7..2897606cfccd62b9f894a34697c9ceb25c09a957 100644 (file)
@@ -1,4 +1,6 @@
-"""test #7656"""
+"""test sessionmaker, originally for #7656"""
+
+from typing import reveal_type
 
 from sqlalchemy import create_engine
 from sqlalchemy import Engine
@@ -7,6 +9,7 @@ from sqlalchemy.ext.asyncio import async_sessionmaker
 from sqlalchemy.ext.asyncio import AsyncEngine
 from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy.ext.asyncio import create_async_engine
+from sqlalchemy.orm import QueryPropertyDescriptor
 from sqlalchemy.orm import scoped_session
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import sessionmaker
@@ -110,3 +113,30 @@ def test_8837_async() -> None:
 
     # EXPECTED_TYPE: AsyncSession
     reveal_type(async_session)
+
+
+# test #9338
+ss_9338 = scoped_session_factory(engine)
+
+# EXPECTED_TYPE: QueryPropertyDescriptor
+reveal_type(ss_9338.query_property())
+qp: QueryPropertyDescriptor = ss_9338.query_property()
+
+
+class Foo:
+    query = qp
+
+
+# EXPECTED_TYPE: Query[Foo]
+reveal_type(Foo.query)
+
+# EXPECTED_TYPE: list[Foo]
+reveal_type(Foo.query.all())
+
+
+class Bar:
+    query: QueryPropertyDescriptor = ss_9338.query_property()
+
+
+# EXPECTED_TYPE: Query[Bar]
+reveal_type(Bar.query)
index 46e5e95703cbec6d574dc50571154f4bee959859..e67f64442a3df4e388686f04a23c60fb704ec349 100644 (file)
@@ -1,4 +1,5 @@
-from typing import Any, Type
+from typing import Any
+from typing import Type
 
 from sqlalchemy.sql.elements import ColumnElement
 from sqlalchemy.sql.type_api import TypeEngine