from .result import Result as Result
from .result import result_tuple as result_tuple
from .result import ScalarResult as ScalarResult
+from .result import TupleResult as TupleResult
from .row import BaseRow as BaseRow
from .row import Row as Row
from .row import RowMapping as RowMapping
from typing import MutableMapping
from typing import NoReturn
from typing import Optional
+from typing import overload
from typing import Tuple
from typing import Type
+from typing import TypeVar
from typing import Union
from .interfaces import _IsolationLevel
from . import ScalarResult
from .interfaces import _AnyExecuteParams
from .interfaces import _AnyMultiExecuteParams
- from .interfaces import _AnySingleExecuteParams
from .interfaces import _CoreAnyExecuteParams
from .interfaces import _CoreMultiExecuteParams
from .interfaces import _CoreSingleExecuteParams
from .interfaces import _DBAPIAnyExecuteParams
- from .interfaces import _DBAPIMultiExecuteParams
from .interfaces import _DBAPISingleExecuteParams
from .interfaces import _ExecuteOptions
from .interfaces import _ExecuteOptionsParameter
from ..pool import PoolProxiedConnection
from ..sql import Executable
from ..sql._typing import _InfoType
- from ..sql.base import SchemaVisitor
from ..sql.compiler import Compiled
from ..sql.ddl import ExecutableDDLElement
from ..sql.ddl import SchemaDropper
from ..sql.ddl import SchemaGenerator
from ..sql.functions import FunctionElement
- from ..sql.schema import ColumnDefault
from ..sql.schema import DefaultGenerator
from ..sql.schema import HasSchemaAttr
from ..sql.schema import SchemaItem
+ from ..sql.selectable import TypedReturnsRows
"""Defines :class:`_engine.Connection` and :class:`_engine.Engine`.
"""
+_T = TypeVar("_T", bound=Any)
_EMPTY_EXECUTION_OPTS: _ExecuteOptions = util.EMPTY_DICT
NO_OPTIONS: Mapping[str, Any] = util.EMPTY_DICT
self._dbapi_connection = None
self.__can_reconnect = False
+ @overload
+ def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> Any:
+ ...
+
def scalar(
self,
statement: Executable,
parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
) -> Any:
r"""Executes a SQL statement construct and returns a scalar object.
execution_options or NO_OPTIONS,
)
+ @overload
+ def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
def scalars(
self,
statement: Executable,
parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> ScalarResult[Any]:
+ ...
+
+ def scalars(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
) -> ScalarResult[Any]:
"""Executes and returns a scalar result set, which yields scalar values
"""
- return self.execute(statement, parameters, execution_options).scalars()
+ return self.execute(
+ statement, parameters, execution_options=execution_options
+ ).scalars()
+
+ @overload
+ def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> CursorResult[_T]:
+ ...
+
+ @overload
+ def execute(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> CursorResult[Any]:
+ ...
def execute(
self,
statement: Executable,
parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
r"""Executes a SQL statement construct and returns a
:class:`_engine.CursorResult`.
func: FunctionElement[Any],
distilled_parameters: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
"""Execute a sql.FunctionElement object."""
return self._execute_clauseelement(
ddl: ExecutableDDLElement,
distilled_parameters: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
"""Execute a schema.DDL object."""
execution_options = ddl._execution_options.merge_with(
elem: Executable,
distilled_parameters: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
"""Execute a sql.ClauseElement object."""
execution_options = elem._execution_options.merge_with(
compiled: Compiled,
distilled_parameters: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter = _EMPTY_EXECUTION_OPTS,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
"""Execute a sql.Compiled object.
TODO: why do we have this? likely deprecate or remove
statement: str,
parameters: Optional[_DBAPIAnyExecuteParams] = None,
execution_options: Optional[_ExecuteOptionsParameter] = None,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
r"""Executes a SQL statement construct and returns a
:class:`_engine.CursorResult`.
execution_options: _ExecuteOptions,
*args: Any,
**kw: Any,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
"""Create an :class:`.ExecutionContext` and execute, returning
a :class:`_engine.CursorResult`."""
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from .result import MergedResult
from .interfaces import ExecutionContext
from .result import _KeyIndexType
from .result import _KeyMapRecType
- from .result import _KeyMapType
from .result import _KeyType
from .result import _ProcessorsType
from ..sql.type_api import _ResultProcessorType
+_T = TypeVar("_T", bound=Any)
+
# metadata entry tuple indexes.
# using raw tuple is faster than namedtuple.
MD_INDEX: Literal[0] = 0 # integer index in cursor.description
return md
def __init__(
- self, parent: CursorResult, cursor_description: _DBAPICursorDescription
+ self,
+ parent: CursorResult[Any],
+ cursor_description: _DBAPICursorDescription,
):
context = parent.context
self._tuplefilter = None
_NO_RESULT_METADATA = _NoResultMetaData()
-class CursorResult(Result):
+class CursorResult(Result[_T]):
"""A Result that is representing state from a DBAPI cursor.
.. versionchanged:: 1.4 The :class:`.CursorResult``
"""
+ __slots__ = (
+ "context",
+ "dialect",
+ "cursor",
+ "cursor_strategy",
+ "_echo",
+ "connection",
+ )
+
_metadata: Union[CursorResultMetaData, _NoResultMetaData]
_no_result_metadata = _NO_RESULT_METADATA
_soft_closed: bool = False
make_row = _make_row_2
else:
make_row = _make_row
-
self._set_memoized_attribute("_row_getter", make_row)
else:
def _raw_row_iterator(self):
return self._fetchiter_impl()
- def merge(self, *others: Result) -> MergedResult:
+ def merge(self, *others: Result[Any]) -> MergedResult[Any]:
merged_result = super().merge(*others)
setup_rowcounts = not self._metadata.returns_rows
if setup_rowcounts:
merged_result.rowcount = sum(
- cast(CursorResult, result).rowcount
+ cast("CursorResult[Any]", result).rowcount
for result in (self,) + others
)
return merged_result
from .base import Connection
from .base import Engine
- from .characteristics import ConnectionCharacteristic
- from .interfaces import _AnyMultiExecuteParams
from .interfaces import _CoreMultiExecuteParams
from .interfaces import _CoreSingleExecuteParams
- from .interfaces import _DBAPIAnyExecuteParams
from .interfaces import _DBAPIMultiExecuteParams
- from .interfaces import _DBAPISingleExecuteParams
from .interfaces import _ExecuteOptions
from .interfaces import _IsolationLevel
from .interfaces import _MutableCoreSingleExecuteParams
from ..sql.compiler import Compiled
from ..sql.compiler import Linting
from ..sql.compiler import ResultColumnsEntry
- from ..sql.compiler import TypeCompiler
from ..sql.dml import DMLState
from ..sql.dml import UpdateBase
from ..sql.elements import BindParameter
- from ..sql.roles import ColumnsClauseRole
from ..sql.schema import Column
- from ..sql.schema import ColumnDefault
from ..sql.type_api import _BindProcessorType
- from ..sql.type_api import _ResultProcessorType
from ..sql.type_api import TypeEngine
# When we're handed literal SQL, ensure it's a SELECT query
result_column_struct: Optional[
Tuple[List[ResultColumnsEntry], bool, bool, bool]
] = None
- returned_default_rows: Optional[List[Row]] = None
+ returned_default_rows: Optional[Sequence[Row[Any]]] = None
execution_options: _ExecuteOptions = util.EMPTY_DICT
if cursor_description is None:
strategy = _cursor._NO_CURSOR_DML
- result = _cursor.CursorResult(self, strategy, cursor_description)
+ result: _cursor.CursorResult[Any] = _cursor.CursorResult(
+ self, strategy, cursor_description
+ )
if self.isinsert:
if self._is_implicit_returning:
if typing.TYPE_CHECKING:
from .base import Connection
- from .interfaces import _CoreAnyExecuteParams
from .interfaces import _CoreMultiExecuteParams
from .interfaces import _CoreSingleExecuteParams
from .interfaces import _DBAPIAnyExecuteParams
multiparams: _CoreMultiExecuteParams,
params: _CoreSingleExecuteParams,
execution_options: _ExecuteOptions,
- result: Result,
+ result: Result[Any],
) -> None:
"""Intercept high level execute() events after execute.
def _get_cache_stats(self) -> str:
raise NotImplementedError()
- def _setup_result_proxy(self) -> CursorResult:
+ def _setup_result_proxy(self) -> CursorResult[Any]:
raise NotImplementedError()
def fire_sequence(self, seq: Sequence_SchemaItem, type_: Integer) -> int:
from typing import Sequence
from typing import Set
from typing import Tuple
+from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
"""represents the kind of row we get from a DBAPI cursor"""
_R = TypeVar("_R", bound=_RowData)
+_T = TypeVar("_T", bound=Any)
+_TP = TypeVar("_TP", bound=Tuple[Any, ...])
_InterimRowType = Union[_R, _RawRowType]
"""a catchall "anything" kind of return type that can be applied
def _getter(
self, key: Any, raiseerr: bool = True
- ) -> Optional[Callable[[Row], Any]]:
+ ) -> Optional[Callable[[Row[Any]], Any]]:
index = self._index_for_key(key, raiseerr)
_tuplefilter=_tuplefilter,
)
- def _contains(self, value: Any, row: Row) -> bool:
+ def _contains(self, value: Any, row: Row[Any]) -> bool:
return value in row._data
def _index_for_key(self, key: Any, raiseerr: bool = True) -> int:
def result_tuple(
fields: Sequence[str], extra: Optional[Any] = None
-) -> Callable[[Iterable[Any]], Row]:
+) -> Callable[[Iterable[Any]], Row[Any]]:
parent = SimpleResultMetaData(fields, extra)
return functools.partial(
Row, parent, parent._processors, parent._keymap, Row._default_key_style
class ResultInternal(InPlaceGenerative, Generic[_R]):
- _real_result: Optional[Result] = None
+ __slots__ = ()
+
+ _real_result: Optional[Result[Any]] = None
_generate_rows: bool = True
_row_logging_fn: Optional[Callable[[Any], Any]]
_source_supports_scalars: bool
- def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row]]:
+ def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row[Any]]]:
raise NotImplementedError()
def _fetchone_impl(
self, hard_close: bool = False
- ) -> Optional[_InterimRowType[Row]]:
+ ) -> Optional[_InterimRowType[Row[Any]]]:
raise NotImplementedError()
def _fetchmany_impl(
self, size: Optional[int] = None
- ) -> List[_InterimRowType[Row]]:
+ ) -> List[_InterimRowType[Row[Any]]]:
raise NotImplementedError()
- def _fetchall_impl(self) -> List[_InterimRowType[Row]]:
+ def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]:
raise NotImplementedError()
def _soft_close(self, hard: bool = False) -> None:
@HasMemoized_ro_memoized_attribute
def _row_getter(self) -> Optional[Callable[..., _R]]:
- real_result: Result = (
- self._real_result if self._real_result else cast(Result, self)
+ real_result: Result[Any] = (
+ self._real_result
+ if self._real_result
+ else cast("Result[Any]", self)
)
if real_result._source_supports_scalars:
keymap: _KeyMapType,
key_style: Any,
scalar_obj: Any,
- ) -> Row:
+ ) -> Row[Any]:
return _proc(
metadata, processors, keymap, key_style, (scalar_obj,)
)
fixed_tf = tf
- def make_row(row: _InterimRowType[Row]) -> _R:
+ def make_row(row: _InterimRowType[Row[Any]]) -> _R:
return _make_row_orig(fixed_tf(row))
else:
if fns:
_make_row = make_row
- def make_row(row: _InterimRowType[Row]) -> _R:
+ def make_row(row: _InterimRowType[Row[Any]]) -> _R:
interim_row = _make_row(row)
for fn in fns:
interim_row = fn(interim_row)
if self._unique_filter_state:
uniques, strategy = self._unique_strategy
- def iterrows(self: Result) -> Iterator[_R]:
+ def iterrows(self: Result[Any]) -> Iterator[_R]:
for raw_row in self._fetchiter_impl():
obj: _InterimRowType[Any] = (
make_row(raw_row) if make_row else raw_row
else:
- def iterrows(self: Result) -> Iterator[_R]:
+ def iterrows(self: Result[Any]) -> Iterator[_R]:
for raw_row in self._fetchiter_impl():
row: _InterimRowType[Any] = (
make_row(raw_row) if make_row else raw_row
if self._unique_filter_state:
uniques, strategy = self._unique_strategy
- def onerow(self: Result) -> Union[_NoRow, _R]:
+ def onerow(self: Result[Any]) -> Union[_NoRow, _R]:
_onerow = self._fetchone_impl
while True:
row = _onerow()
else:
- def onerow(self: Result) -> Union[_NoRow, _R]:
+ def onerow(self: Result[Any]) -> Union[_NoRow, _R]:
row = self._fetchone_impl()
if row is None:
return _NO_ROW
real_result = (
self._real_result
if self._real_result
- else cast(Result, self)
+ else cast("Result[Any]", self)
)
if real_result._yield_per:
num_required = num = real_result._yield_per
real_result = (
self._real_result
if self._real_result
- else cast(Result, self)
+ else cast("Result[Any]", self)
)
num = real_result._yield_per
self: SelfResultInternal, indexes: Sequence[_KeyIndexType]
) -> SelfResultInternal:
real_result = (
- self._real_result if self._real_result else cast(Result, self)
+ self._real_result
+ if self._real_result
+ else cast("Result[Any]", self)
)
if not real_result._source_supports_scalars or len(indexes) != 1:
real_result = (
self._real_result
if self._real_result is not None
- else cast(Result, self)
+ else cast("Result[Any]", self)
)
if not strategy and self._metadata._unique_filters:
class _WithKeys:
+ __slots__ = ()
+
_metadata: ResultMetaData
# used mainly to share documentation on the keys method.
return self._metadata.keys
-SelfResult = TypeVar("SelfResult", bound="Result")
+SelfResult = TypeVar("SelfResult", bound="Result[Any]")
-class Result(_WithKeys, ResultInternal[Row]):
+class Result(_WithKeys, ResultInternal[Row[_TP]]):
"""Represent a set of database results.
.. versionadded:: 1.4 The :class:`.Result` object provides a completely
"""
- _row_logging_fn: Optional[Callable[[Row], Row]] = None
+ __slots__ = ("_metadata", "__dict__")
+
+ _row_logging_fn: Optional[Callable[[Row[Any]], Row[Any]]] = None
_source_supports_scalars: bool = False
appropriate :class:`.ColumnElement` objects which correspond to
a given statement construct.
+ .. versionchanged:: 2.0 Due to a bug in 1.4, the
+ :meth:`.Result.columns` method had an incorrect behavior where
+ calling upon the method with just one index would cause the
+ :class:`.Result` object to yield scalar values rather than
+ :class:`.Row` objects. In version 2.0, this behavior has been
+ corrected such that calling upon :meth:`.Result.columns` with
+ a single index will produce a :class:`.Result` object that continues
+ to yield :class:`.Row` objects, which include only a single column.
+
E.g.::
statement = select(table.c.x, table.c.y, table.c.z)
"""
return self._column_slices(col_expressions)
+ @overload
+ def scalars(self: Result[Tuple[_T]]) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(
+ self: Result[Tuple[_T]], index: Literal[0]
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]:
+ ...
+
def scalars(self, index: _KeyIndexType = 0) -> ScalarResult[Any]:
"""Return a :class:`_result.ScalarResult` filtering object which
will return single elements rather than :class:`_row.Row` objects.
def _getter(
self, key: _KeyIndexType, raiseerr: bool = True
- ) -> Optional[Callable[[Row], Any]]:
+ ) -> Optional[Callable[[Row[Any]], Any]]:
"""return a callable that will retrieve the given key from a
:class:`.Row`.
return MappingResult(self)
+ @property
+ def t(self) -> TupleResult[_TP]:
+ """Apply a "typed tuple" typing filter to returned rows.
+
+ The :attr:`.Result.t` attribute is a synonym for calling the
+ :meth:`.Result.tuples` method.
+
+ .. versionadded:: 2.0
+
+ """
+ return self # type: ignore
+
+ def tuples(self) -> TupleResult[_TP]:
+ """Apply a "typed tuple" typing filter to returned rows.
+
+ This method returns the same :class:`.Result` object at runtime,
+ however annotates as returning a :class:`.TupleResult` object
+ that will indicate to :pep:`484` typing tools that plain typed
+ ``Tuple`` instances are returned rather than rows. This allows
+ tuple unpacking and ``__getitem__`` access of :class:`.Row` objects
+ to by typed, for those cases where the statement invoked itself
+ included typing information.
+
+ .. versionadded:: 2.0
+
+ :return: the :class:`_result.TupleResult` type at typing time.
+
+ .. seealso::
+
+ :attr:`.Result.t` - shorter synonym
+
+ :attr:`.Row.t` - :class:`.Row` version
+
+ """
+
+ return self # type: ignore
+
def _raw_row_iterator(self) -> Iterator[_RowData]:
"""Return a safe iterator that yields raw row data.
"""
raise NotImplementedError()
- def __iter__(self) -> Iterator[Row]:
+ def __iter__(self) -> Iterator[Row[_TP]]:
return self._iter_impl()
- def __next__(self) -> Row:
+ def __next__(self) -> Row[_TP]:
return self._next_impl()
- def partitions(self, size: Optional[int] = None) -> Iterator[List[Row]]:
+ def partitions(
+ self, size: Optional[int] = None
+ ) -> Iterator[Sequence[Row[_TP]]]:
"""Iterate through sub-lists of rows of the size given.
Each list will be of the size given, excluding the last list to
else:
break
- def fetchall(self) -> List[Row]:
+ def fetchall(self) -> Sequence[Row[_TP]]:
"""A synonym for the :meth:`_engine.Result.all` method."""
return self._allrows()
- def fetchone(self) -> Optional[Row]:
+ def fetchone(self) -> Optional[Row[_TP]]:
"""Fetch one row.
When all rows are exhausted, returns None.
else:
return row
- def fetchmany(self, size: Optional[int] = None) -> List[Row]:
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[Row[_TP]]:
"""Fetch many rows.
When all rows are exhausted, returns an empty list.
return self._manyrow_getter(self, size)
- def all(self) -> List[Row]:
+ def all(self) -> Sequence[Row[_TP]]:
"""Return all rows in a list.
Closes the result set after invocation. Subsequent invocations
return self._allrows()
- def first(self) -> Optional[Row]:
+ def first(self) -> Optional[Row[_TP]]:
"""Fetch the first row or None if no row is present.
Closes the result set and discards remaining rows.
raise_for_second_row=False, raise_for_none=False, scalar=False
)
- def one_or_none(self) -> Optional[Row]:
+ def one_or_none(self) -> Optional[Row[_TP]]:
"""Return at most one result or raise an exception.
Returns ``None`` if the result has no rows.
raise_for_second_row=True, raise_for_none=False, scalar=False
)
+ @overload
+ def scalar_one(self: Result[Tuple[_T]]) -> _T:
+ ...
+
+ @overload
+ def scalar_one(self) -> Any:
+ ...
+
def scalar_one(self) -> Any:
"""Return exactly one scalar result or raise an exception.
raise_for_second_row=True, raise_for_none=True, scalar=True
)
+ @overload
+ def scalar_one_or_none(self: Result[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar_one_or_none(self) -> Optional[Any]:
+ ...
+
def scalar_one_or_none(self) -> Optional[Any]:
"""Return exactly one or no scalar result.
raise_for_second_row=True, raise_for_none=False, scalar=True
)
- def one(self) -> Row:
+ def one(self) -> Row[_TP]:
"""Return exactly one row or raise an exception.
Raises :class:`.NoResultFound` if the result returns no
raise_for_second_row=True, raise_for_none=True, scalar=False
)
+ @overload
+ def scalar(self: Result[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar(self) -> Any:
+ ...
+
def scalar(self) -> Any:
"""Fetch the first column of the first row, and close the result set.
raise_for_second_row=False, raise_for_none=False, scalar=True
)
- def freeze(self) -> FrozenResult:
+ def freeze(self) -> FrozenResult[_TP]:
"""Return a callable object that will produce copies of this
:class:`.Result` when invoked.
return FrozenResult(self)
- def merge(self, *others: Result) -> MergedResult:
+ def merge(self, *others: Result[Any]) -> MergedResult[_TP]:
"""Merge this :class:`.Result` with other compatible result
objects.
"""
- _post_creational_filter: Optional[Callable[[Any], Any]] = None
+ __slots__ = (
+ "_real_result",
+ "_post_creational_filter",
+ "_metadata",
+ "_unique_filter_state",
+ "__dict__",
+ )
+
+ _post_creational_filter: Optional[Callable[[Any], Any]]
- _real_result: Result
+ _real_result: Result[Any]
def _soft_close(self, hard: bool = False) -> None:
self._real_result._soft_close(hard=hard)
def _attributes(self) -> Dict[Any, Any]:
return self._real_result._attributes
- def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row]]:
+ def _fetchiter_impl(self) -> Iterator[_InterimRowType[Row[Any]]]:
return self._real_result._fetchiter_impl()
def _fetchone_impl(
self, hard_close: bool = False
- ) -> Optional[_InterimRowType[Row]]:
+ ) -> Optional[_InterimRowType[Row[Any]]]:
return self._real_result._fetchone_impl(hard_close=hard_close)
- def _fetchall_impl(self) -> List[_InterimRowType[Row]]:
+ def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]:
return self._real_result._fetchall_impl()
def _fetchmany_impl(
self, size: Optional[int] = None
- ) -> List[_InterimRowType[Row]]:
+ ) -> List[_InterimRowType[Row[Any]]]:
return self._real_result._fetchmany_impl(size=size)
"""
+ __slots__ = ()
+
_generate_rows = False
_post_creational_filter: Optional[Callable[[Any], Any]]
- def __init__(self, real_result: Result, index: _KeyIndexType):
+ def __init__(self, real_result: Result[Any], index: _KeyIndexType):
self._real_result = real_result
if real_result._source_supports_scalars:
self._unique_filter_state = (set(), strategy)
return self
- def partitions(self, size: Optional[int] = None) -> Iterator[List[_R]]:
+ def partitions(self, size: Optional[int] = None) -> Iterator[Sequence[_R]]:
"""Iterate through sub-lists of elements of the size given.
Equivalent to :meth:`_result.Result.partitions` except that
else:
break
- def fetchall(self) -> List[_R]:
+ def fetchall(self) -> Sequence[_R]:
"""A synonym for the :meth:`_engine.ScalarResult.all` method."""
return self._allrows()
- def fetchmany(self, size: Optional[int] = None) -> List[_R]:
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
"""Fetch many objects.
Equivalent to :meth:`_result.Result.fetchmany` except that
"""
return self._manyrow_getter(self, size)
- def all(self) -> List[_R]:
+ def all(self) -> Sequence[_R]:
"""Return all scalar values in a list.
Equivalent to :meth:`_result.Result.all` except that
)
+SelfTupleResult = TypeVar("SelfTupleResult", bound="TupleResult[Any]")
+
+
+class TupleResult(FilterResult[_R], util.TypingOnly):
+ """a :class:`.Result` that's typed as returning plain Python tuples
+ instead of rows.
+
+ Since :class:`.Row` acts like a tuple in every way already,
+ this class is a typing only class, regular :class:`.Result` is still
+ used at runtime.
+
+ """
+
+ __slots__ = ()
+
+ if TYPE_CHECKING:
+
+ def partitions(
+ self, size: Optional[int] = None
+ ) -> Iterator[Sequence[_R]]:
+ """Iterate through sub-lists of elements of the size given.
+
+ Equivalent to :meth:`_result.Result.partitions` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ def fetchone(self) -> Optional[_R]:
+ """Fetch one tuple.
+
+ Equivalent to :meth:`_result.Result.fetchone` except that
+ tuple values, rather than :class:`_result.Row`
+ objects, are returned.
+
+ """
+ ...
+
+ def fetchall(self) -> Sequence[_R]:
+ """A synonym for the :meth:`_engine.ScalarResult.all` method."""
+ ...
+
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
+ """Fetch many objects.
+
+ Equivalent to :meth:`_result.Result.fetchmany` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ def all(self) -> Sequence[_R]: # noqa: A001
+ """Return all scalar values in a list.
+
+ Equivalent to :meth:`_result.Result.all` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ def __iter__(self) -> Iterator[_R]:
+ ...
+
+ def __next__(self) -> _R:
+ ...
+
+ def first(self) -> Optional[_R]:
+ """Fetch the first object or None if no object is present.
+
+ Equivalent to :meth:`_result.Result.first` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+
+ """
+ ...
+
+ def one_or_none(self) -> Optional[_R]:
+ """Return at most one object or raise an exception.
+
+ Equivalent to :meth:`_result.Result.one_or_none` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ def one(self) -> _R:
+ """Return exactly one object or raise an exception.
+
+ Equivalent to :meth:`_result.Result.one` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ @overload
+ def scalar_one(self: TupleResult[Tuple[_T]]) -> _T:
+ ...
+
+ @overload
+ def scalar_one(self) -> Any:
+ ...
+
+ def scalar_one(self) -> Any:
+ """Return exactly one scalar result or raise an exception.
+
+ This is equivalent to calling :meth:`.Result.scalars` and then
+ :meth:`.Result.one`.
+
+ .. seealso::
+
+ :meth:`.Result.one`
+
+ :meth:`.Result.scalars`
+
+ """
+ ...
+
+ @overload
+ def scalar_one_or_none(self: TupleResult[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar_one_or_none(self) -> Optional[Any]:
+ ...
+
+ def scalar_one_or_none(self) -> Optional[Any]:
+ """Return exactly one or no scalar result.
+
+ This is equivalent to calling :meth:`.Result.scalars` and then
+ :meth:`.Result.one_or_none`.
+
+ .. seealso::
+
+ :meth:`.Result.one_or_none`
+
+ :meth:`.Result.scalars`
+
+ """
+ ...
+
+ @overload
+ def scalar(self: TupleResult[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar(self) -> Any:
+ ...
+
+ def scalar(self) -> Any:
+ """Fetch the first column of the first row, and close the result set.
+
+ Returns None if there are no rows to fetch.
+
+ No validation is performed to test if additional rows remain.
+
+ After calling this method, the object is fully closed,
+ e.g. the :meth:`_engine.CursorResult.close`
+ method will have been called.
+
+ :return: a Python scalar value , or None if no rows remain.
+
+ """
+ ...
+
+
SelfMappingResult = TypeVar("SelfMappingResult", bound="MappingResult")
"""
+ __slots__ = ()
+
_generate_rows = True
_post_creational_filter = operator.attrgetter("_mapping")
- def __init__(self, result: Result):
+ def __init__(self, result: Result[Any]):
self._real_result = result
self._unique_filter_state = result._unique_filter_state
self._metadata = result._metadata
def partitions(
self, size: Optional[int] = None
- ) -> Iterator[List[RowMapping]]:
+ ) -> Iterator[Sequence[RowMapping]]:
"""Iterate through sub-lists of elements of the size given.
Equivalent to :meth:`_result.Result.partitions` except that
else:
break
- def fetchall(self) -> List[RowMapping]:
+ def fetchall(self) -> Sequence[RowMapping]:
"""A synonym for the :meth:`_engine.MappingResult.all` method."""
return self._allrows()
else:
return row
- def fetchmany(self, size: Optional[int] = None) -> List[RowMapping]:
+ def fetchmany(self, size: Optional[int] = None) -> Sequence[RowMapping]:
"""Fetch many objects.
Equivalent to :meth:`_result.Result.fetchmany` except that
return self._manyrow_getter(self, size)
- def all(self) -> List[RowMapping]:
+ def all(self) -> Sequence[RowMapping]:
"""Return all scalar values in a list.
Equivalent to :meth:`_result.Result.all` except that
)
-class FrozenResult:
+class FrozenResult(Generic[_TP]):
"""Represents a :class:`.Result` object in a "frozen" state suitable
for caching.
data: Sequence[Any]
- def __init__(self, result: Result):
+ def __init__(self, result: Result[_TP]):
self.metadata = result._metadata._for_freeze()
self._source_supports_scalars = result._source_supports_scalars
self._attributes = result._attributes
else:
return [list(row) for row in self.data]
- def with_new_rows(self, tuple_data: Sequence[Row]) -> FrozenResult:
+ def with_new_rows(
+ self, tuple_data: Sequence[Row[_TP]]
+ ) -> FrozenResult[_TP]:
fr = FrozenResult.__new__(FrozenResult)
fr.metadata = self.metadata
fr._attributes = self._attributes
fr.data = tuple_data
return fr
- def __call__(self) -> Result:
- result = IteratorResult(self.metadata, iter(self.data))
+ def __call__(self) -> Result[_TP]:
+ result: IteratorResult[_TP] = IteratorResult(
+ self.metadata, iter(self.data)
+ )
result._attributes = self._attributes
result._source_supports_scalars = self._source_supports_scalars
return result
-class IteratorResult(Result):
+class IteratorResult(Result[_TP]):
"""A :class:`.Result` that gets data from a Python iterator of
:class:`.Row` objects or similar row-like data.
def _fetchone_impl(
self, hard_close: bool = False
- ) -> Optional[_InterimRowType[Row]]:
+ ) -> Optional[_InterimRowType[Row[Any]]]:
if self._hard_closed:
self._raise_hard_closed()
else:
return row
- def _fetchall_impl(self) -> List[_InterimRowType[Row]]:
+ def _fetchall_impl(self) -> List[_InterimRowType[Row[Any]]]:
if self._hard_closed:
self._raise_hard_closed()
try:
def _fetchmany_impl(
self, size: Optional[int] = None
- ) -> List[_InterimRowType[Row]]:
+ ) -> List[_InterimRowType[Row[Any]]]:
if self._hard_closed:
self._raise_hard_closed()
return list(itertools.islice(self.iterator, 0, size))
-def null_result() -> IteratorResult:
+def null_result() -> IteratorResult[Any]:
return IteratorResult(SimpleResultMetaData([]), iter([]))
SelfChunkedIteratorResult = TypeVar(
- "SelfChunkedIteratorResult", bound="ChunkedIteratorResult"
+ "SelfChunkedIteratorResult", bound="ChunkedIteratorResult[Any]"
)
-class ChunkedIteratorResult(IteratorResult):
+class ChunkedIteratorResult(IteratorResult[_TP]):
"""An :class:`.IteratorResult` that works from an iterator-producing callable.
The given ``chunks`` argument is a function that is given a number of rows
def _fetchmany_impl(
self, size: Optional[int] = None
- ) -> List[_InterimRowType[Row]]:
+ ) -> List[_InterimRowType[Row[Any]]]:
if self.dynamic_yield_per:
self.iterator = itertools.chain.from_iterable(self.chunks(size))
return super()._fetchmany_impl(size=size)
-class MergedResult(IteratorResult):
+class MergedResult(IteratorResult[_TP]):
"""A :class:`_engine.Result` that is merged from any number of
:class:`_engine.Result` objects.
rowcount: Optional[int]
def __init__(
- self, cursor_metadata: ResultMetaData, results: Sequence[Result]
+ self, cursor_metadata: ResultMetaData, results: Sequence[Result[_TP]]
):
self._results = results
super(MergedResult, self).__init__(
from typing import Any
from typing import Callable
from typing import Dict
+from typing import Generic
from typing import Iterator
from typing import List
from typing import Mapping
from typing import overload
from typing import Sequence
from typing import Tuple
+from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from ..sql import util as sql_util
from ..util._has_cy import HAS_CYEXTENSION
-if typing.TYPE_CHECKING or not HAS_CYEXTENSION:
+if TYPE_CHECKING or not HAS_CYEXTENSION:
from ._py_row import BaseRow as BaseRow
from ._py_row import KEY_INTEGER_ONLY
from ._py_row import KEY_OBJECTS_ONLY
from sqlalchemy.cyextension.resultproxy import KEY_INTEGER_ONLY
from sqlalchemy.cyextension.resultproxy import KEY_OBJECTS_ONLY
-if typing.TYPE_CHECKING:
+if TYPE_CHECKING:
from .result import _KeyType
from .result import RMKeyView
from ..sql.type_api import _ResultProcessorType
+_T = TypeVar("_T", bound=Any)
+_TP = TypeVar("_TP", bound=Tuple[Any, ...])
-class Row(BaseRow, typing.Sequence[Any]):
+
+class Row(BaseRow, Sequence[Any], Generic[_TP]):
"""Represent a single result row.
The :class:`.Row` object represents a row of a database result. It is
def __delattr__(self, name: str) -> NoReturn:
raise AttributeError("can't delete attribute")
+ def tuple(self) -> _TP:
+ """Return a 'tuple' form of this :class:`.Row`.
+
+ At runtime, this method returns "self"; the :class:`.Row` object is
+ already a named tuple. However, at the typing level, if this
+ :class:`.Row` is typed, the "tuple" return type will be a :pep:`484`
+ ``Tuple`` datatype that contains typing information about individual
+ elements, supporting typed unpacking and attribute access.
+
+ .. versionadded:: 2.0
+
+ .. seealso::
+
+ :meth:`.Result.tuples`
+
+ """
+ return self # type: ignore
+
+ @property
+ def t(self) -> _TP:
+ """a synonym for :attr:`.Row.tuple`
+
+ .. versionadded:: 2.0
+
+ .. seealso::
+
+ :meth:`.Result.t`
+
+ """
+ return self # type: ignore
+
@property
def _mapping(self) -> RowMapping:
"""Return a :class:`.RowMapping` for this :class:`.Row`.
def _filter_on_values(
self, filters: Optional[Sequence[Optional[_ResultProcessorType[Any]]]]
- ) -> Row:
+ ) -> Row[Any]:
return Row(
self._parent,
filters,
self._data,
)
- if not typing.TYPE_CHECKING:
+ if not TYPE_CHECKING:
def _special_name_accessor(name: str) -> Any:
"""Handle ambiguous names such as "count" and "index" """
__hash__ = BaseRow.__hash__
- if typing.TYPE_CHECKING:
+ if TYPE_CHECKING:
@overload
def __getitem__(self, index: int) -> Any:
_default_key_style = KEY_OBJECTS_ONLY
- if typing.TYPE_CHECKING:
+ if TYPE_CHECKING:
def __getitem__(self, key: _KeyType) -> Any:
...
from typing import NoReturn
from typing import Optional
from typing import overload
+from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from . import exc as async_exc
from ...pool import PoolProxiedConnection
from ...sql._typing import _InfoType
from ...sql.base import Executable
+ from ...sql.selectable import TypedReturnsRows
+
+_T = TypeVar("_T", bound=Any)
class _SyncConnectionCallable(Protocol):
statement: str,
parameters: Optional[_DBAPIAnyExecuteParams] = None,
execution_options: Optional[_ExecuteOptionsParameter] = None,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
r"""Executes a driver-level SQL string and return buffered
:class:`_engine.Result`.
return await _ensure_sync_result(result, self.exec_driver_sql)
+ @overload
+ async def stream(
+ self,
+ statement: TypedReturnsRows[_T],
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> AsyncResult[_T]:
+ ...
+
+ @overload
async def stream(
self,
statement: Executable,
parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
- ) -> AsyncResult:
+ ) -> AsyncResult[Any]:
+ ...
+
+ async def stream(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> AsyncResult[Any]:
"""Execute a statement and return a streaming
:class:`_asyncio.AsyncResult` object."""
self._proxied.execute,
statement,
parameters,
- util.EMPTY_DICT.merge_with(
+ execution_options=util.EMPTY_DICT.merge_with(
execution_options, {"stream_results": True}
),
_require_await=True,
assert False, "server side result expected"
return AsyncResult(result)
+ @overload
+ async def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> CursorResult[_T]:
+ ...
+
+ @overload
async def execute(
self,
statement: Executable,
parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
+ ...
+
+ async def execute(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> CursorResult[Any]:
r"""Executes a SQL statement construct and return a buffered
:class:`_engine.Result`.
self._proxied.execute,
statement,
parameters,
- execution_options,
+ execution_options=execution_options,
_require_await=True,
)
return await _ensure_sync_result(result, self.execute)
+ @overload
+ async def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
async def scalar(
self,
statement: Executable,
parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> Any:
+ ...
+
+ async def scalar(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
) -> Any:
r"""Executes a SQL statement construct and returns a scalar object.
first row returned.
"""
- result = await self.execute(statement, parameters, execution_options)
+ result = await self.execute(
+ statement, parameters, execution_options=execution_options
+ )
return result.scalar()
+ @overload
+ async def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ async def scalars(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> ScalarResult[Any]:
+ ...
+
async def scalars(
self,
statement: Executable,
parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
) -> ScalarResult[Any]:
r"""Executes a SQL statement construct and returns a scalar objects.
.. versionadded:: 1.4.24
"""
- result = await self.execute(statement, parameters, execution_options)
+ result = await self.execute(
+ statement, parameters, execution_options=execution_options
+ )
return result.scalars()
+ @overload
+ async def stream_scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> AsyncScalarResult[_T]:
+ ...
+
+ @overload
async def stream_scalars(
self,
statement: Executable,
parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: Optional[_ExecuteOptionsParameter] = None,
+ ) -> AsyncScalarResult[Any]:
+ ...
+
+ async def stream_scalars(
+ self,
+ statement: Executable,
+ parameters: Optional[_CoreSingleExecuteParams] = None,
+ *,
execution_options: Optional[_ExecuteOptionsParameter] = None,
) -> AsyncScalarResult[Any]:
r"""Executes a SQL statement and returns a streaming scalar result
.. versionadded:: 1.4.24
"""
- result = await self.stream(statement, parameters, execution_options)
+ result = await self.stream(
+ statement, parameters, execution_options=execution_options
+ )
return result.scalars()
async def run_sync(
import operator
from typing import Any
from typing import AsyncIterator
-from typing import List
from typing import Optional
+from typing import overload
+from typing import Sequence
+from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypeVar
from . import exc as async_exc
+from ... import util
from ...engine.result import _NO_ROW
from ...engine.result import _R
from ...engine.result import FilterResult
from ...engine.row import Row
from ...engine.row import RowMapping
from ...util.concurrency import greenlet_spawn
+from ...util.typing import Literal
if TYPE_CHECKING:
from ...engine import CursorResult
from ...engine.result import _UniqueFilterType
from ...engine.result import RMKeyView
+_T = TypeVar("_T", bound=Any)
+_TP = TypeVar("_TP", bound=Tuple[Any, ...])
+
class AsyncCommon(FilterResult[_R]):
- _real_result: Result
+ __slots__ = ()
+
+ _real_result: Result[Any]
_metadata: ResultMetaData
async def close(self) -> None:
await greenlet_spawn(self._real_result.close)
-SelfAsyncResult = TypeVar("SelfAsyncResult", bound="AsyncResult")
+SelfAsyncResult = TypeVar("SelfAsyncResult", bound="AsyncResult[Any]")
-class AsyncResult(AsyncCommon[Row]):
+class AsyncResult(AsyncCommon[Row[_TP]]):
"""An asyncio wrapper around a :class:`_result.Result` object.
The :class:`_asyncio.AsyncResult` only applies to statement executions that
"""
- def __init__(self, real_result: Result):
+ __slots__ = ()
+
+ _real_result: Result[_TP]
+
+ def __init__(self, real_result: Result[_TP]):
self._real_result = real_result
self._metadata = real_result._metadata
self._unique_filter_state = real_result._unique_filter_state
+ self._post_creational_filter = None
# BaseCursorResult pre-generates the "_row_getter". Use that
# if available rather than building a second one
"_row_getter", real_result.__dict__["_row_getter"]
)
+ @property
+ def t(self) -> AsyncTupleResult[_TP]:
+ """Apply a "typed tuple" typing filter to returned rows.
+
+ The :attr:`.AsyncResult.t` attribute is a synonym for calling the
+ :meth:`.AsyncResult.tuples` method.
+
+ .. versionadded:: 2.0
+
+ """
+ return self # type: ignore
+
+ def tuples(self) -> AsyncTupleResult[_TP]:
+ """Apply a "typed tuple" typing filter to returned rows.
+
+ This method returns the same :class:`.AsyncResult` object at runtime,
+ however annotates as returning a :class:`.AsyncTupleResult` object
+ that will indicate to :pep:`484` typing tools that plain typed
+ ``Tuple`` instances are returned rather than rows. This allows
+ tuple unpacking and ``__getitem__`` access of :class:`.Row` objects
+ to by typed, for those cases where the statement invoked itself
+ included typing information.
+
+ .. versionadded:: 2.0
+
+ :return: the :class:`_result.AsyncTupleResult` type at typing time.
+
+ .. seealso::
+
+ :attr:`.AsyncResult.t` - shorter synonym
+
+ :attr:`.Row.t` - :class:`.Row` version
+
+ """
+
+ return self # type: ignore
+
def keys(self) -> RMKeyView:
"""Return the :meth:`_engine.Result.keys` collection from the
underlying :class:`_engine.Result`.
async def partitions(
self, size: Optional[int] = None
- ) -> AsyncIterator[List[Row]]:
+ ) -> AsyncIterator[Sequence[Row[_TP]]]:
"""Iterate through sub-lists of rows of the size given.
An async iterator is returned::
else:
break
- async def fetchone(self) -> Optional[Row]:
+ async def fetchall(self) -> Sequence[Row[_TP]]:
+ """A synonym for the :meth:`.AsyncResult.all` method.
+
+ .. versionadded:: 2.0
+
+ """
+
+ return await greenlet_spawn(self._allrows)
+
+ async def fetchone(self) -> Optional[Row[_TP]]:
"""Fetch one row.
When all rows are exhausted, returns None.
else:
return row
- async def fetchmany(self, size: Optional[int] = None) -> List[Row]:
+ async def fetchmany(
+ self, size: Optional[int] = None
+ ) -> Sequence[Row[_TP]]:
"""Fetch many rows.
When all rows are exhausted, returns an empty list.
return await greenlet_spawn(self._manyrow_getter, self, size)
- async def all(self) -> List[Row]:
+ async def all(self) -> Sequence[Row[_TP]]:
"""Return all rows in a list.
Closes the result set after invocation. Subsequent invocations
return await greenlet_spawn(self._allrows)
- def __aiter__(self) -> AsyncResult:
+ def __aiter__(self) -> AsyncResult[_TP]:
return self
- async def __anext__(self) -> Row:
+ async def __anext__(self) -> Row[_TP]:
row = await greenlet_spawn(self._onerow_getter, self)
if row is _NO_ROW:
raise StopAsyncIteration()
else:
return row
- async def first(self) -> Optional[Row]:
+ async def first(self) -> Optional[Row[_TP]]:
"""Fetch the first row or None if no row is present.
Closes the result set and discards remaining rows.
"""
return await greenlet_spawn(self._only_one_row, False, False, False)
- async def one_or_none(self) -> Optional[Row]:
+ async def one_or_none(self) -> Optional[Row[_TP]]:
"""Return at most one result or raise an exception.
Returns ``None`` if the result has no rows.
"""
return await greenlet_spawn(self._only_one_row, True, False, False)
+ @overload
+ async def scalar_one(self: AsyncResult[Tuple[_T]]) -> _T:
+ ...
+
+ @overload
+ async def scalar_one(self) -> Any:
+ ...
+
async def scalar_one(self) -> Any:
"""Return exactly one scalar result or raise an exception.
"""
return await greenlet_spawn(self._only_one_row, True, True, True)
+ @overload
+ async def scalar_one_or_none(
+ self: AsyncResult[Tuple[_T]],
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar_one_or_none(self) -> Optional[Any]:
+ ...
+
async def scalar_one_or_none(self) -> Optional[Any]:
"""Return exactly one or no scalar result.
"""
return await greenlet_spawn(self._only_one_row, True, False, True)
- async def one(self) -> Row:
+ async def one(self) -> Row[_TP]:
"""Return exactly one row or raise an exception.
Raises :class:`.NoResultFound` if the result returns no
"""
return await greenlet_spawn(self._only_one_row, True, True, False)
+ @overload
+ async def scalar(self: AsyncResult[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar(self) -> Any:
+ ...
+
async def scalar(self) -> Any:
"""Fetch the first column of the first row, and close the result set.
"""
return await greenlet_spawn(self._only_one_row, False, False, True)
- async def freeze(self) -> FrozenResult:
+ async def freeze(self) -> FrozenResult[_TP]:
"""Return a callable object that will produce copies of this
:class:`_asyncio.AsyncResult` when invoked.
return await greenlet_spawn(FrozenResult, self)
- def merge(self, *others: AsyncResult) -> MergedResult:
+ def merge(self, *others: AsyncResult[_TP]) -> MergedResult[_TP]:
"""Merge this :class:`_asyncio.AsyncResult` with other compatible result
objects.
(self._real_result,) + tuple(o._real_result for o in others),
)
+ @overload
+ def scalars(
+ self: AsyncResult[Tuple[_T]], index: Literal[0]
+ ) -> AsyncScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(self: AsyncResult[Tuple[_T]]) -> AsyncScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]:
+ ...
+
def scalars(self, index: _KeyIndexType = 0) -> AsyncScalarResult[Any]:
"""Return an :class:`_asyncio.AsyncScalarResult` filtering object which
will return single elements rather than :class:`_row.Row` objects.
"""
+ __slots__ = ()
+
_generate_rows = False
- def __init__(self, real_result: Result, index: _KeyIndexType):
+ def __init__(self, real_result: Result[Any], index: _KeyIndexType):
self._real_result = real_result
if real_result._source_supports_scalars:
async def partitions(
self, size: Optional[int] = None
- ) -> AsyncIterator[List[_R]]:
+ ) -> AsyncIterator[Sequence[_R]]:
"""Iterate through sub-lists of elements of the size given.
Equivalent to :meth:`_asyncio.AsyncResult.partitions` except that
else:
break
- async def fetchall(self) -> List[_R]:
+ async def fetchall(self) -> Sequence[_R]:
"""A synonym for the :meth:`_asyncio.AsyncScalarResult.all` method."""
return await greenlet_spawn(self._allrows)
- async def fetchmany(self, size: Optional[int] = None) -> List[_R]:
+ async def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
"""Fetch many objects.
Equivalent to :meth:`_asyncio.AsyncResult.fetchmany` except that
"""
return await greenlet_spawn(self._manyrow_getter, self, size)
- async def all(self) -> List[_R]:
+ async def all(self) -> Sequence[_R]:
"""Return all scalar values in a list.
Equivalent to :meth:`_asyncio.AsyncResult.all` except that
"""
+ __slots__ = ()
+
_generate_rows = True
_post_creational_filter = operator.attrgetter("_mapping")
- def __init__(self, result: Result):
+ def __init__(self, result: Result[Any]):
self._real_result = result
self._unique_filter_state = result._unique_filter_state
self._metadata = result._metadata
async def partitions(
self, size: Optional[int] = None
- ) -> AsyncIterator[List[RowMapping]]:
+ ) -> AsyncIterator[Sequence[RowMapping]]:
"""Iterate through sub-lists of elements of the size given.
else:
break
- async def fetchall(self) -> List[RowMapping]:
+ async def fetchall(self) -> Sequence[RowMapping]:
"""A synonym for the :meth:`_asyncio.AsyncMappingResult.all` method."""
return await greenlet_spawn(self._allrows)
else:
return row
- async def fetchmany(self, size: Optional[int] = None) -> List[RowMapping]:
+ async def fetchmany(
+ self, size: Optional[int] = None
+ ) -> Sequence[RowMapping]:
"""Fetch many rows.
Equivalent to :meth:`_asyncio.AsyncResult.fetchmany` except that
return await greenlet_spawn(self._manyrow_getter, self, size)
- async def all(self) -> List[RowMapping]:
+ async def all(self) -> Sequence[RowMapping]:
"""Return all rows in a list.
Equivalent to :meth:`_asyncio.AsyncResult.all` except that
return await greenlet_spawn(self._only_one_row, True, True, False)
-_RT = TypeVar("_RT", bound="Result")
+SelfAsyncTupleResult = TypeVar(
+ "SelfAsyncTupleResult", bound="AsyncTupleResult[Any]"
+)
+
+
+class AsyncTupleResult(AsyncCommon[_R], util.TypingOnly):
+ """a :class:`.AsyncResult` that's typed as returning plain Python tuples
+ instead of rows.
+
+ Since :class:`.Row` acts like a tuple in every way already,
+ this class is a typing only class, regular :class:`.AsyncResult` is
+ still used at runtime.
+
+ """
+
+ __slots__ = ()
+
+ if TYPE_CHECKING:
+
+ async def partitions(
+ self, size: Optional[int] = None
+ ) -> AsyncIterator[Sequence[_R]]:
+ """Iterate through sub-lists of elements of the size given.
+
+ Equivalent to :meth:`_result.Result.partitions` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ async def fetchone(self) -> Optional[_R]:
+ """Fetch one tuple.
+
+ Equivalent to :meth:`_result.Result.fetchone` except that
+ tuple values, rather than :class:`_result.Row`
+ objects, are returned.
+
+ """
+ ...
+
+ async def fetchall(self) -> Sequence[_R]:
+ """A synonym for the :meth:`_engine.ScalarResult.all` method."""
+ ...
+
+ async def fetchmany(self, size: Optional[int] = None) -> Sequence[_R]:
+ """Fetch many objects.
+
+ Equivalent to :meth:`_result.Result.fetchmany` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ async def all(self) -> Sequence[_R]: # noqa: A001
+ """Return all scalar values in a list.
+
+ Equivalent to :meth:`_result.Result.all` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ async def __aiter__(self) -> AsyncIterator[_R]:
+ ...
+
+ async def __anext__(self) -> _R:
+ ...
+
+ async def first(self) -> Optional[_R]:
+ """Fetch the first object or None if no object is present.
+
+ Equivalent to :meth:`_result.Result.first` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+
+ """
+ ...
+
+ async def one_or_none(self) -> Optional[_R]:
+ """Return at most one object or raise an exception.
+
+ Equivalent to :meth:`_result.Result.one_or_none` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ async def one(self) -> _R:
+ """Return exactly one object or raise an exception.
+
+ Equivalent to :meth:`_result.Result.one` except that
+ tuple values, rather than :class:`_result.Row` objects,
+ are returned.
+
+ """
+ ...
+
+ @overload
+ async def scalar_one(self: AsyncTupleResult[Tuple[_T]]) -> _T:
+ ...
+
+ @overload
+ async def scalar_one(self) -> Any:
+ ...
+
+ async def scalar_one(self) -> Any:
+ """Return exactly one scalar result or raise an exception.
+
+ This is equivalent to calling :meth:`.Result.scalars` and then
+ :meth:`.Result.one`.
+
+ .. seealso::
+
+ :meth:`.Result.one`
+
+ :meth:`.Result.scalars`
+
+ """
+ ...
+
+ @overload
+ async def scalar_one_or_none(
+ self: AsyncTupleResult[Tuple[_T]],
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar_one_or_none(self) -> Optional[Any]:
+ ...
+
+ async def scalar_one_or_none(self) -> Optional[Any]:
+ """Return exactly one or no scalar result.
+
+ This is equivalent to calling :meth:`.Result.scalars` and then
+ :meth:`.Result.one_or_none`.
+
+ .. seealso::
+
+ :meth:`.Result.one_or_none`
+
+ :meth:`.Result.scalars`
+
+ """
+ ...
+
+ @overload
+ async def scalar(self: AsyncTupleResult[Tuple[_T]]) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar(self) -> Any:
+ ...
+
+ async def scalar(self) -> Any:
+ """Fetch the first column of the first row, and close the result set.
+
+ Returns None if there are no rows to fetch.
+
+ No validation is performed to test if additional rows remain.
+
+ After calling this method, the object is fully closed,
+ e.g. the :meth:`_engine.CursorResult.close`
+ method will have been called.
+
+ :return: a Python scalar value , or None if no rows remain.
+
+ """
+ ...
+
+
+_RT = TypeVar("_RT", bound="Result[Any]")
async def _ensure_sync_result(result: _RT, calling_method: Any) -> _RT:
- cursor_result: CursorResult
+ cursor_result: CursorResult[Any]
try:
is_cursor = result._is_cursor
from typing import Iterable
from typing import Iterator
from typing import Optional
+from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from .session import async_sessionmaker
from ...engine import Engine
from ...engine import Result
from ...engine import Row
+ from ...engine import RowMapping
from ...engine.interfaces import _CoreAnyExecuteParams
from ...engine.interfaces import _CoreSingleExecuteParams
- from ...engine.interfaces import _ExecuteOptions
from ...engine.interfaces import _ExecuteOptionsParameter
from ...engine.result import ScalarResult
from ...orm._typing import _IdentityKeyType
from ...sql.base import Executable
from ...sql.elements import ClauseElement
from ...sql.selectable import ForUpdateArg
+ from ...sql.selectable import TypedReturnsRows
+
+_T = TypeVar("_T", bound=Any)
@create_proxy_methods(
return await self._proxied.delete(instance)
+ @overload
+ async def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[_T]:
+ ...
+
+ @overload
+ async def execute(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[Any]:
+ ...
+
async def execute(
self,
statement: Executable,
execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
- ) -> Result:
+ ) -> Result[Any]:
r"""Execute a statement and return a buffered
:class:`_engine.Result` object.
return await self._proxied.rollback()
+ @overload
+ async def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Any:
+ ...
+
async def scalar(
self,
statement: Executable,
**kw,
)
+ @overload
+ async def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ async def scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[Any]:
+ ...
+
async def scalars(
self,
statement: Executable,
**kw,
)
+ @overload
+ async def stream(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncResult[_T]:
+ ...
+
+ @overload
async def stream(
self,
statement: Executable,
execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
- ) -> AsyncResult:
+ ) -> AsyncResult[Any]:
+ ...
+
+ async def stream(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncResult[Any]:
r"""Execute a statement and return a streaming
:class:`_asyncio.AsyncResult` object.
**kw,
)
+ @overload
+ async def stream_scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncScalarResult[_T]:
+ ...
+
+ @overload
+ async def stream_scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncScalarResult[Any]:
+ ...
+
async def stream_scalars(
self,
statement: Executable,
ident: Union[Any, Tuple[Any, ...]] = None,
*,
instance: Optional[Any] = None,
- row: Optional[Row] = None,
+ row: Optional[Union[Row[Any], RowMapping]] = None,
identity_token: Optional[Any] = None,
) -> _IdentityKeyType[Any]:
r"""Return an identity key.
from typing import Iterator
from typing import NoReturn
from typing import Optional
+from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from . import engine
from ...engine import Engine
from ...engine import Result
from ...engine import Row
+ from ...engine import RowMapping
from ...engine import ScalarResult
- from ...engine import Transaction
from ...engine.interfaces import _CoreAnyExecuteParams
from ...engine.interfaces import _CoreSingleExecuteParams
- from ...engine.interfaces import _ExecuteOptions
from ...engine.interfaces import _ExecuteOptionsParameter
from ...event import dispatcher
from ...orm._typing import _IdentityKeyType
from ...sql.base import Executable
from ...sql.elements import ClauseElement
from ...sql.selectable import ForUpdateArg
+ from ...sql.selectable import TypedReturnsRows
_AsyncSessionBind = Union["AsyncEngine", "AsyncConnection"]
+_T = TypeVar("_T", bound=Any)
+
class _SyncSessionCallable(Protocol):
def __call__(self, session: Session, *arg: Any, **kw: Any) -> Any:
return await greenlet_spawn(fn, self.sync_session, *arg, **kw)
+ @overload
+ async def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[_T]:
+ ...
+
+ @overload
+ async def execute(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[Any]:
+ ...
+
async def execute(
self,
statement: Executable,
execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
- ) -> Result:
+ ) -> Result[Any]:
"""Execute a statement and return a buffered
:class:`_engine.Result` object.
)
return await _ensure_sync_result(result, self.execute)
+ @overload
+ async def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ async def scalar(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Any:
+ ...
+
async def scalar(
self,
statement: Executable,
)
return result
+ @overload
+ async def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ async def scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[Any]:
+ ...
+
async def scalars(
self,
statement: Executable,
)
return result_obj
+ @overload
+ async def stream(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncResult[_T]:
+ ...
+
+ @overload
+ async def stream(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncResult[Any]:
+ ...
+
async def stream(
self,
statement: Executable,
execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
bind_arguments: Optional[_BindArguments] = None,
**kw: Any,
- ) -> AsyncResult:
+ ) -> AsyncResult[Any]:
"""Execute a statement and return a streaming
:class:`_asyncio.AsyncResult` object.
)
return AsyncResult(result)
+ @overload
+ async def stream_scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncScalarResult[_T]:
+ ...
+
+ @overload
+ async def stream_scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> AsyncScalarResult[Any]:
+ ...
+
async def stream_scalars(
self,
statement: Executable,
ident: Union[Any, Tuple[Any, ...]] = None,
*,
instance: Optional[Any] = None,
- row: Optional[Row] = None,
+ row: Optional[Union[Row[Any], RowMapping]] = None,
identity_token: Optional[Any] = None,
) -> _IdentityKeyType[Any]:
r"""Return an identity key.
from ..orm import collections
from ..orm import exc as orm_exc
from ..orm import instrumentation as orm_instrumentation
+from ..orm import util as orm_util
from ..orm.instrumentation import _default_dict_getter
from ..orm.instrumentation import _default_manager_getter
from ..orm.instrumentation import _default_opt_manager_getter
attributes.manager_of_class
) = orm_instrumentation.manager_of_class = manager_of_class
orm_base.opt_manager_of_class = (
+ orm_util.opt_manager_of_class
+ ) = (
attributes.opt_manager_of_class
) = orm_instrumentation.opt_manager_of_class = opt_manager_of_class
from ..sql.base import SchemaEventTarget
from ..sql.schema import SchemaConst
from ..sql.selectable import FromClause
+from ..util.typing import Annotated
from ..util.typing import Literal
if TYPE_CHECKING:
from ._typing import _ORMColumnExprArgument
from .descriptor_props import _CompositeAttrType
from .interfaces import PropComparator
+ from .mapper import Mapper
from .query import Query
from .relationships import _LazyLoadArgumentType
from .relationships import _ORMBackrefArgument
mapperlib._dispose_registries(mapperlib._all_registries(), False)
+# I would really like a way to get the Type[] here that shows up
+# in a different way in typing tools, however there is no current method
+# that is accepted by mypy (subclass of Type[_O] works in pylance, rejected
+# by mypy).
+AliasedType = Annotated[Type[_O], "aliased"]
+
+
+@overload
+def aliased(
+ element: Type[_O],
+ alias: Optional[Union[Alias, Subquery]] = None,
+ name: Optional[str] = None,
+ flat: bool = False,
+ adapt_on_names: bool = False,
+) -> AliasedType[_O]:
+ ...
+
+
@overload
def aliased(
- element: _EntityType[_O],
+ element: Union[AliasedClass[_O], Mapper[_O], AliasedInsp[_O]],
alias: Optional[Union[Alias, Subquery]] = None,
name: Optional[str] = None,
flat: bool = False,
name: Optional[str] = None,
flat: bool = False,
adapt_on_names: bool = False,
-) -> Union[AliasedClass[_O], FromClause]:
+) -> Union[AliasedClass[_O], FromClause, AliasedType[_O]]:
"""Produce an alias of the given element, usually an :class:`.AliasedClass`
instance.
my_alias = aliased(MyClass)
- session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id)
+ stmt = select(MyClass, my_alias).filter(MyClass.id > my_alias.id)
+ result = session.execute(stmt)
The :func:`.aliased` function is used to create an ad-hoc mapping of a
mapped class to a new selectable. By default, a selectable is generated
.. seealso::
+ :class:`.AsAliased` - a :pep:`484` typed version of
+ :func:`_orm.aliased`
+
:ref:`tutorial_orm_entity_aliases` - in the :ref:`unified_tutorial`
:ref:`orm_queryguide_orm_aliases` - in the :ref:`queryguide_toplevel`
from .. import inspection
from .. import util
from ..sql import base as sql_base
+from ..sql import cache_key
from ..sql import roles
from ..sql import traversals
from ..sql import visitors
traversals.HasCopyInternals,
roles.JoinTargetRole,
roles.OnClauseRole,
- roles.ColumnsClauseRole,
- roles.ExpressionElementRole[_T],
sql_base.Immutable,
- sql_base.MemoizedHasCacheKey,
+ cache_key.MemoizedHasCacheKey,
):
"""Base class for :term:`descriptor` objects that intercept
attribute events on behalf of a :class:`.MapperProperty`
from .. import exc as sa_exc
from .. import inspection
from .. import util
+from ..sql import roles
from ..sql.elements import SQLCoreOperations
from ..util import FastIntFlag
from ..util.langhelpers import TypingOnly
return mapper
-@inspection._inspects(type)
-def _inspect_mc(class_: Type[_O]) -> Optional[Mapper[_O]]:
- try:
- class_manager = opt_manager_of_class(class_)
- if class_manager is None or not class_manager.is_mapped:
- return None
- mapper = class_manager.mapper
- except exc.NO_STATE:
- return None
- else:
- return mapper
-
-
def _parse_mapper_argument(arg: Union[Mapper[_O], Type[_O]]) -> Mapper[_O]:
insp = inspection.inspect(arg, raiseerr=False)
if insp_is_mapper(insp):
...
-class Mapped(ORMDescriptor[_T], TypingOnly):
+class Mapped(ORMDescriptor[_T], roles.TypedColumnsClauseRole[_T], TypingOnly):
"""Represent an ORM mapped attribute on a mapped class.
This class represents the complete descriptor interface for any class
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from . import attributes
from ..sql.base import CacheableOptions
from ..sql.base import CompileState
from ..sql.base import Executable
+from ..sql.base import Generative
from ..sql.base import Options
from ..sql.dml import UpdateBase
from ..sql.elements import GroupedElement
from ..sql.elements import TextClause
+from ..sql.selectable import ExecutableReturnsRows
from ..sql.selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
from ..sql.selectable import LABEL_STYLE_NONE
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
-from ..sql.selectable import ReturnsRows
from ..sql.selectable import Select
from ..sql.selectable import SelectLabelStyle
from ..sql.selectable import SelectState
from ..sql.selectable import SelectBase
from ..sql.type_api import TypeEngine
+_T = TypeVar("_T", bound=Any)
_path_registry = PathRegistry.root
_EMPTY_DICT = util.immutabledict()
return None
-class FromStatement(GroupedElement, ReturnsRows, Executable):
+class FromStatement(GroupedElement, Generative, ExecutableReturnsRows):
"""Core construct that represents a load of ORM objects from various
:class:`.ReturnsRows` and other classes including:
from ..sql.type_api import TypeEngine
from ..util.typing import TypedDict
-
if typing.TYPE_CHECKING:
from ._typing import _EntityType
from ._typing import _IdentityKeyType
)
-class ORMColumnsClauseRole(roles.ColumnsClauseRole):
+class ORMColumnsClauseRole(roles.TypedColumnsClauseRole[_T]):
__slots__ = ()
_role_name = "ORM mapped entity, aliased entity, or Column expression"
-class ORMEntityColumnsClauseRole(ORMColumnsClauseRole):
+class ORMEntityColumnsClauseRole(ORMColumnsClauseRole[_T]):
__slots__ = ()
_role_name = "ORM mapped or aliased entity"
# into "type" is a bad idea
type: Union[Type[Any], TypeEngine[Any]]
aliased: bool
- expr: _ColumnsClauseArgument
- entity: Optional[_ColumnsClauseArgument]
+ expr: _ColumnsClauseArgument[Any]
+ entity: Optional[_ColumnsClauseArgument[Any]]
class _IntrospectsAnnotations:
query_entity: _MapperEntity,
path: PathRegistry,
mapper: Mapper[Any],
- result: Result,
+ result: Result[Any],
adapter: Optional[ColumnAdapter],
populators: _PopulatorDict,
) -> None:
path: AbstractEntityRegistry,
loadopt: Optional[_LoadElement],
mapper: Mapper[Any],
- result: Result,
+ result: Result[Any],
adapter: Optional[ORMAdapter],
populators: _PopulatorDict,
) -> None:
from .descriptor_props import Synonym
from .events import MapperEvents
from .instrumentation import ClassManager
- from .path_registry import AbstractEntityRegistry
from .path_registry import CachingEntityRegistry
from .properties import ColumnProperty
from .relationships import Relationship
from ..sql.base import ReadOnlyColumnCollection
from ..sql.elements import ColumnClause
from ..sql.elements import ColumnElement
+ from ..sql.elements import KeyedColumnElement
from ..sql.schema import Column
from ..sql.schema import Table
from ..sql.selectable import FromClause
- from ..sql.selectable import TableClause
from ..sql.util import ColumnAdapter
from ..util import OrderedSet
@log.class_logger
class Mapper(
ORMFromClauseRole,
- ORMEntityColumnsClauseRole,
+ ORMEntityColumnsClauseRole[_O],
MemoizedHasCacheKey,
InspectionAttr,
log.Identified,
"""
- polymorphic_on: Optional[ColumnElement[Any]]
+ polymorphic_on: Optional[KeyedColumnElement[Any]]
"""The :class:`_schema.Column` or SQL expression specified as the
``polymorphic_on`` argument
for this :class:`_orm.Mapper`, within an inheritance scenario.
instrument = True
key = getattr(col, "key", None)
if key:
- if self._should_exclude(col.key, col.key, False, col):
+ if self._should_exclude(key, key, False, col):
raise sa_exc.InvalidRequestError(
"Cannot exclude or override the "
- "discriminator column %r" % col.key
+ "discriminator column %r" % key
)
else:
self.polymorphic_on = col = col.label("_sa_polymorphic_on")
def identity_key_from_row(
self,
- row: Optional[Union[Row, RowMapping]],
+ row: Optional[Union[Row[Any], RowMapping]],
identity_token: Optional[Any] = None,
adapter: Optional[ColumnAdapter] = None,
) -> _IdentityKeyType[_O]:
if TYPE_CHECKING:
from ._typing import _ORMColumnExprArgument
from ..sql._typing import _InfoType
- from ..sql.elements import ColumnElement
+ from ..sql.elements import KeyedColumnElement
_T = TypeVar("_T", bound=Any)
_PT = TypeVar("_PT", bound=Any)
inherit_cache = True
_links_to_entity = False
- columns: List[ColumnElement[Any]]
+ columns: List[KeyedColumnElement[Any]]
+ _orig_columns: List[KeyedColumnElement[Any]]
_is_polymorphic_discriminator: bool
from typing import Iterable
from typing import List
from typing import Optional
+from typing import overload
+from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypeVar
from . import interfaces
from . import loading
from . import util as orm_util
+from ._typing import _O
from .base import _assertions
from .context import _column_descriptions
from .context import _determine_last_joined_entity
from .. import sql
from .. import util
from ..engine import Result
+from ..engine import Row
from ..sql import coercions
from ..sql import expression
from ..sql import roles
from ..sql import util as sql_util
from ..sql import visitors
from ..sql._typing import _FromClauseArgument
+from ..sql._typing import _TP
from ..sql.annotation import SupportsCloneAnnotations
from ..sql.base import _entity_namespace_key
from ..sql.base import _generative
from ..sql.base import Executable
+from ..sql.base import Generative
from ..sql.expression import Exists
from ..sql.selectable import _MemoizedSelectEntities
from ..sql.selectable import _SelectFromElements
from ..sql.selectable import HasPrefixes
from ..sql.selectable import HasSuffixes
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
+from ..util.typing import Literal
if TYPE_CHECKING:
+ from ._typing import _EntityType
+ from .session import Session
+ from ..engine.result import ScalarResult
+ from ..engine.row import Row
+ from ..sql._typing import _ColumnExpressionArgument
+ from ..sql._typing import _ColumnsClauseArgument
+ from ..sql._typing import _MAYBE_ENTITY
+ from ..sql._typing import _no_kw
+ from ..sql._typing import _NOT_ENTITY
+ from ..sql._typing import _PropagateAttrsType
+ from ..sql._typing import _T0
+ from ..sql._typing import _T1
+ from ..sql._typing import _T2
+ from ..sql._typing import _T3
+ from ..sql._typing import _T4
+ from ..sql._typing import _T5
+ from ..sql._typing import _T6
+ from ..sql._typing import _T7
+ from ..sql._typing import _TypedColumnClauseArgument as _TCCA
+ from ..sql.roles import TypedColumnsClauseRole
from ..sql.selectable import _SetupJoinsElement
from ..sql.selectable import Alias
+ from ..sql.selectable import ExecutableReturnsRows
+ from ..sql.selectable import ScalarSelect
from ..sql.selectable import Subquery
__all__ = ["Query", "QueryContext"]
HasSuffixes,
HasHints,
log.Identified,
+ Generative,
Executable,
Generic[_T],
):
# mirrors that of ClauseElement, used to propagate the "orm"
# plugin as well as the "subject" of the plugin, e.g. the mapper
# we are querying against.
- _propagate_attrs = util.immutabledict()
+ @util.memoized_property
+ def _propagate_attrs(self) -> _PropagateAttrsType:
+ return util.EMPTY_DICT
- def __init__(self, entities, session=None):
+ def __init__(
+ self,
+ entities: Sequence[_ColumnsClauseArgument[Any]],
+ session: Optional[Session] = None,
+ ):
"""Construct a :class:`_query.Query` directly.
E.g.::
for ent in util.to_list(entities)
]
+ @overload
+ def tuples(self: Query[Row[_TP]]) -> Query[_TP]:
+ ...
+
+ @overload
+ def tuples(self: Query[_O]) -> Query[Tuple[_O]]:
+ ...
+
+ def tuples(self) -> Query[Any]:
+ """return a tuple-typed form of this :class:`.Query`.
+
+ This method invokes the :meth:`.Query.only_return_tuples`
+ method with a value of ``True``, which by itself ensures that this
+ :class:`.Query` will always return :class:`.Row` objects, even
+ if the query is made against a single entity. It then also
+ at the typing level will return a "typed" query, if possible,
+ that will type result rows as ``Tuple`` objects with typed
+ elements.
+
+ This method can be compared to the :meth:`.Result.tuples` method,
+ which returns "self", but from a typing perspective returns an object
+ that will yield typed ``Tuple`` objects for results. Typing
+ takes effect only if this :class:`.Query` object is a typed
+ query object already.
+
+ .. versionadded:: 2.0
+
+ """
+ return self.only_return_tuples(True)
+
def _entity_from_pre_ent_zero(self):
if not self._raw_columns:
return None
return self.enable_eagerloads(False).statement.label(name)
+ @overload
+ def as_scalar(
+ self: Query[Tuple[_MAYBE_ENTITY]],
+ ) -> ScalarSelect[_MAYBE_ENTITY]:
+ ...
+
+ @overload
+ def as_scalar(
+ self: Query[Tuple[_NOT_ENTITY]],
+ ) -> ScalarSelect[_NOT_ENTITY]:
+ ...
+
+ @overload
+ def as_scalar(self) -> ScalarSelect[Any]:
+ ...
+
@util.deprecated(
"1.4",
"The :meth:`_query.Query.as_scalar` method is deprecated and will be "
"removed in a future release. Please refer to "
":meth:`_query.Query.scalar_subquery`.",
)
- def as_scalar(self):
+ def as_scalar(self) -> ScalarSelect[Any]:
"""Return the full SELECT statement represented by this
:class:`_query.Query`, converted to a scalar subquery.
"""
return self.scalar_subquery()
- def scalar_subquery(self):
+ @overload
+ def scalar_subquery(
+ self: Query[Tuple[_MAYBE_ENTITY]],
+ ) -> ScalarSelect[Any]:
+ ...
+
+ @overload
+ def scalar_subquery(
+ self: Query[Tuple[_NOT_ENTITY]],
+ ) -> ScalarSelect[_NOT_ENTITY]:
+ ...
+
+ @overload
+ def scalar_subquery(self) -> ScalarSelect[Any]:
+ ...
+
+ def scalar_subquery(self) -> ScalarSelect[Any]:
"""Return the full SELECT statement represented by this
:class:`_query.Query`, converted to a scalar subquery.
.statement
)
- @_generative
- def only_return_tuples(self: SelfQuery, value) -> SelfQuery:
- """When set to True, the query results will always be a tuple.
+ @overload
+ def only_return_tuples(
+ self: Query[_O], value: Literal[True]
+ ) -> RowReturningQuery[Tuple[_O]]:
+ ...
- This is specifically for single element queries. The default is False.
+ @overload
+ def only_return_tuples(
+ self: Query[_O], value: Literal[False]
+ ) -> Query[_O]:
+ ...
- .. versionadded:: 1.2.5
+ @_generative
+ def only_return_tuples(self, value: bool) -> Query[Any]:
+ """When set to True, the query results will always be a
+ :class:`.Row` object.
+
+ This can change a query that normally returns a single entity
+ as a scalar to return a :class:`.Row` result in all cases.
.. seealso::
+ :meth:`.Query.tuples` - returns tuples, but also at the typing
+ level will type results as ``Tuple``.
+
:meth:`_query.Query.is_single_entity`
"""
return self.filter(with_parent(instance, property, entity_zero.entity))
@_generative
- def add_entity(self: SelfQuery, entity, alias=None) -> SelfQuery:
+ def add_entity(
+ self,
+ entity: _EntityType[Any],
+ alias: Optional[Union[Alias, Subquery]] = None,
+ ) -> Query[Any]:
"""add a mapped entity to the list of result columns
to be returned."""
except StopIteration:
return None
+ @overload
+ def with_entities(
+ self, _entity: _EntityType[_O], **kwargs: Any
+ ) -> ScalarInstanceQuery[_O]:
+ ...
+
+ @overload
+ def with_entities(
+ self, _colexpr: TypedColumnsClauseRole[_T]
+ ) -> RowReturningQuery[Tuple[_T]]:
+ ...
+
+ # START OVERLOADED FUNCTIONS self.with_entities RowReturningQuery 2-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def with_entities(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> RowReturningQuery[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def with_entities(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def with_entities(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def with_entities(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def with_entities(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def with_entities(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def with_entities(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.with_entities
+
+ @overload
+ def with_entities(
+ self: SelfQuery, *entities: _ColumnsClauseArgument[Any]
+ ) -> SelfQuery:
+ ...
+
@_generative
- def with_entities(self: SelfQuery, *entities) -> SelfQuery:
+ def with_entities(
+ self: SelfQuery, *entities: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> SelfQuery:
r"""Return a new :class:`_query.Query`
replacing the SELECT list with the
given entities.
limit(1)
"""
+ if __kw:
+ raise _no_kw()
_MemoizedSelectEntities._generate_for_statement(self)
self._set_entities(entities)
return self
@_generative
- def add_columns(self: SelfQuery, *column) -> SelfQuery:
+ def add_columns(self, *column: _ColumnExpressionArgument) -> Query[Any]:
"""Add one or more column expressions to the list
of result columns to be returned."""
"is deprecated and will be removed in a "
"future release. Please use :meth:`_query.Query.add_columns`",
)
- def add_column(self, column):
+ def add_column(self, column) -> Query[Any]:
"""Add a column expression to the list of result columns to be
returned.
@_generative
@_assertions(_no_statement_condition, _no_limit_offset)
- def filter(self: SelfQuery, *criterion) -> SelfQuery:
+ def filter(
+ self: SelfQuery, *criterion: _ColumnExpressionArgument[bool]
+ ) -> SelfQuery:
r"""Apply the given filtering criterion to a copy
of this :class:`_query.Query`, using SQL expressions.
return self._raw_columns[0]
- def filter_by(self, **kwargs):
+ def filter_by(self: SelfQuery, **kwargs: Any) -> SelfQuery:
r"""Apply the given filtering criterion to a copy
of this :class:`_query.Query`, using keyword expressions.
@_generative
@_assertions(_no_statement_condition, _no_limit_offset)
- def order_by(self: SelfQuery, *clauses) -> SelfQuery:
+ def order_by(
+ self: SelfQuery, *clauses: _ColumnExpressionArgument[Any]
+ ) -> SelfQuery:
"""Apply one or more ORDER BY criteria to the query and return
the newly resulting :class:`_query.Query`.
@_generative
@_assertions(_no_statement_condition, _no_limit_offset)
- def group_by(self: SelfQuery, *clauses) -> SelfQuery:
+ def group_by(
+ self: SelfQuery, *clauses: _ColumnExpressionArgument[Any]
+ ) -> SelfQuery:
"""Apply one or more GROUP BY criterion to the query and return
the newly resulting :class:`_query.Query`.
@_generative
@_assertions(_no_statement_condition, _no_limit_offset)
- def having(self: SelfQuery, criterion) -> SelfQuery:
+ def having(
+ self: SelfQuery, *having: _ColumnExpressionArgument[bool]
+ ) -> SelfQuery:
r"""Apply a HAVING criterion to the query and return the
newly resulting :class:`_query.Query`.
"""
- self._having_criteria += (
- coercions.expect(
- roles.WhereHavingRole, criterion, apply_propagate_attrs=self
- ),
- )
+ for criterion in having:
+ having_criteria = coercions.expect(
+ roles.WhereHavingRole, criterion
+ )
+ self._having_criteria += (having_criteria,)
return self
def _set_op(self, expr_fn, *q):
return self._from_selectable(expr_fn(*([self] + list(q))).subquery())
- def union(self, *q):
+ def union(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce a UNION of this Query against one or more queries.
e.g.::
"""
return self._set_op(expression.union, *q)
- def union_all(self, *q):
+ def union_all(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce a UNION ALL of this Query against one or more queries.
Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
"""
return self._set_op(expression.union_all, *q)
- def intersect(self, *q):
+ def intersect(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce an INTERSECT of this Query against one or more queries.
Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
"""
return self._set_op(expression.intersect, *q)
- def intersect_all(self, *q):
+ def intersect_all(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce an INTERSECT ALL of this Query against one or more queries.
Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
"""
return self._set_op(expression.intersect_all, *q)
- def except_(self, *q):
+ def except_(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce an EXCEPT of this Query against one or more queries.
Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
"""
return self._set_op(expression.except_, *q)
- def except_all(self, *q):
+ def except_all(self: SelfQuery, *q: Query[Any]) -> SelfQuery:
"""Produce an EXCEPT ALL of this Query against one or more queries.
Works the same way as :meth:`~sqlalchemy.orm.query.Query.union`. See
@_generative
@_assertions(_no_clauseelement_condition)
- def from_statement(self: SelfQuery, statement) -> SelfQuery:
+ def from_statement(
+ self: SelfQuery, statement: ExecutableReturnsRows
+ ) -> SelfQuery:
"""Execute the given SELECT statement and return results.
This method bypasses all internal statement compilation, and the
:meth:`_query.Query.one_or_none`
"""
- return self._iter().one()
+ return self._iter().one() # type: ignore
def scalar(self) -> Any:
"""Return the first element of the first result or None
def __iter__(self) -> Iterable[_T]:
return self._iter().__iter__()
- def _iter(self):
+ def _iter(self) -> Union[ScalarResult[_T], Result[_T]]:
# new style execution.
params = self._params
class BulkDelete(BulkUD):
"""BulkUD which handles DELETEs."""
+
+
+class RowReturningQuery(Query[Row[_TP]]):
+ pass
from typing import Iterable
from typing import Iterator
from typing import Optional
+from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TypeVar
from typing import Union
-from . import exc as orm_exc
-from .base import class_mapper
from .session import Session
from .. import exc as sa_exc
from .. import util
from ..util.typing import Protocol
if TYPE_CHECKING:
+ from ._typing import _EntityType
from ._typing import _IdentityKeyType
from .identity import IdentityMap
from .interfaces import ORMOption
from .mapper import Mapper
from .query import Query
+ from .query import RowReturningQuery
from .session import _BindArguments
from .session import _EntityBindKey
from .session import _PKIdentityArgument
from ..engine import Engine
from ..engine import Result
from ..engine import Row
+ from ..engine import RowMapping
from ..engine.interfaces import _CoreAnyExecuteParams
from ..engine.interfaces import _CoreSingleExecuteParams
from ..engine.interfaces import _ExecuteOptions
from ..engine.interfaces import _ExecuteOptionsParameter
from ..engine.result import ScalarResult
from ..sql._typing import _ColumnsClauseArgument
+ from ..sql._typing import _T0
+ from ..sql._typing import _T1
+ from ..sql._typing import _T2
+ from ..sql._typing import _T3
+ from ..sql._typing import _T4
+ from ..sql._typing import _T5
+ from ..sql._typing import _T6
+ from ..sql._typing import _T7
+ from ..sql._typing import _TypedColumnClauseArgument as _TCCA
from ..sql.base import Executable
from ..sql.elements import ClauseElement
+ from ..sql.roles import TypedColumnsClauseRole
from ..sql.selectable import ForUpdateArg
+ from ..sql.selectable import TypedReturnsRows
+
+_T = TypeVar("_T", bound=Any)
class _QueryDescriptorType(Protocol):
- def __get__(self, instance: Any, owner: Type[Any]) -> Optional[Query[Any]]:
+ def __get__(self, instance: Any, owner: Type[_T]) -> Query[_T]:
...
self.registry.clear()
def query_property(
- self, query_cls: Optional[Type[Query[Any]]] = None
+ self, query_cls: Optional[Type[Query[_T]]] = None
) -> _QueryDescriptorType:
"""return a class property which produces a :class:`_query.Query`
object
"""
class query:
- def __get__(
- s, instance: Any, owner: Type[Any]
- ) -> Optional[Query[Any]]:
- try:
- mapper = class_mapper(owner)
- assert mapper is not None
- if query_cls:
- # custom query class
- return query_cls(mapper, session=self.registry())
- else:
- # session's configured query class
- return self.registry().query(mapper)
- except orm_exc.UnmappedClassError:
- return None
+ def __get__(s, instance: Any, owner: Type[_O]) -> Query[_O]:
+ if query_cls:
+ # custom query class
+ return query_cls(owner, session=self.registry()) # type: ignore # noqa: E501
+ else:
+ # session's configured query class
+ return self.registry().query(owner)
return query()
return self._proxied.delete(instance)
+ @overload
+ def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[_T]:
+ ...
+
+ @overload
+ def execute(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[Any]:
+ ...
+
def execute(
self,
statement: Executable,
bind_arguments: Optional[_BindArguments] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
- ) -> Result:
+ ) -> Result[Any]:
r"""Execute a SQL expression construct.
.. container:: class_bases
return self._proxied.merge(instance, load=load, options=options)
+ @overload
+ def query(self, _entity: _EntityType[_O]) -> Query[_O]:
+ ...
+
+ @overload
def query(
- self, *entities: _ColumnsClauseArgument, **kwargs: Any
+ self, _colexpr: TypedColumnsClauseRole[_T]
+ ) -> RowReturningQuery[Tuple[_T]]:
+ ...
+
+ # START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def query(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> RowReturningQuery[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def query(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.query
+
+ @overload
+ def query(
+ self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
+ ) -> Query[Any]:
+ ...
+
+ def query(
+ self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
) -> Query[Any]:
r"""Return a new :class:`_query.Query` object corresponding to this
:class:`_orm.Session`.
return self._proxied.rollback()
+ @overload
+ def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Any:
+ ...
+
def scalar(
self,
statement: Executable,
**kw,
)
+ @overload
+ def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[Any]:
+ ...
+
def scalars(
self,
statement: Executable,
ident: Union[Any, Tuple[Any, ...]] = None,
*,
instance: Optional[Any] = None,
- row: Optional[Row] = None,
+ row: Optional[Union[Row[Any], RowMapping]] = None,
identity_token: Optional[Any] = None,
) -> _IdentityKeyType[Any]:
r"""Return an identity key.
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
import weakref
from ..util.typing import Protocol
if typing.TYPE_CHECKING:
+ from ._typing import _EntityType
from ._typing import _IdentityKeyType
from ._typing import _InstanceDict
+ from ._typing import _O
+ from .context import FromStatement
from .interfaces import ORMOption
from .interfaces import UserDefinedOption
from .mapper import Mapper
from .path_registry import PathRegistry
+ from .query import RowReturningQuery
from ..engine import Result
from ..engine import Row
from ..engine import RowMapping
from ..event import _InstanceLevelDispatch
from ..sql._typing import _ColumnsClauseArgument
from ..sql._typing import _InfoType
+ from ..sql._typing import _T0
+ from ..sql._typing import _T1
+ from ..sql._typing import _T2
+ from ..sql._typing import _T3
+ from ..sql._typing import _T4
+ from ..sql._typing import _T5
+ from ..sql._typing import _T6
+ from ..sql._typing import _T7
+ from ..sql._typing import _TypedColumnClauseArgument as _TCCA
from ..sql.base import Executable
from ..sql.elements import ClauseElement
+ from ..sql.roles import TypedColumnsClauseRole
from ..sql.schema import Table
- from ..sql.selectable import TableClause
+ from ..sql.selectable import Select
+ from ..sql.selectable import TypedReturnsRows
+
+_T = TypeVar("_T", bound=Any)
__all__ = [
"Session",
ident: Union[Any, Tuple[Any, ...]] = None,
*,
instance: Optional[Any] = None,
- row: Optional[Union[Row, RowMapping]] = None,
+ row: Optional[Union[Row[Any], RowMapping]] = None,
identity_token: Optional[Any] = None,
) -> _IdentityKeyType[Any]:
"""Return an identity key.
params: Optional[_CoreAnyExecuteParams] = None,
execution_options: Optional[_ExecuteOptionsParameter] = None,
bind_arguments: Optional[_BindArguments] = None,
- ) -> Result:
+ ) -> Result[Any]:
"""Execute the statement represented by this
:class:`.ORMExecuteState`, without re-invoking events that have
already proceeded.
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
_scalar_result: bool = ...,
- ) -> Result:
+ ) -> Result[Any]:
...
def _execute_internal(
)
for idx, fn in enumerate(events_todo):
orm_exec_state._starting_event_idx = idx
- fn_result: Optional[Result] = fn(orm_exec_state)
+ fn_result: Optional[Result[Any]] = fn(orm_exec_state)
if fn_result:
if _scalar_result:
return fn_result.scalar()
if _scalar_result and not compile_state_cls:
if TYPE_CHECKING:
params = cast(_CoreSingleExecuteParams, params)
- return conn.scalar(statement, params or {}, execution_options)
+ return conn.scalar(
+ statement, params or {}, execution_options=execution_options
+ )
- result: Result = conn.execute(
- statement, params or {}, execution_options
+ result: Result[Any] = conn.execute(
+ statement, params or {}, execution_options=execution_options
)
if compile_state_cls:
else:
return result
+ @overload
+ def execute(
+ self,
+ statement: TypedReturnsRows[_T],
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[_T]:
+ ...
+
+ @overload
+ def execute(
+ self,
+ statement: Executable,
+ params: Optional[_CoreAnyExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ _parent_execute_state: Optional[Any] = None,
+ _add_event: Optional[Any] = None,
+ ) -> Result[Any]:
+ ...
+
def execute(
self,
statement: Executable,
bind_arguments: Optional[_BindArguments] = None,
_parent_execute_state: Optional[Any] = None,
_add_event: Optional[Any] = None,
- ) -> Result:
+ ) -> Result[Any]:
r"""Execute a SQL expression construct.
Returns a :class:`_engine.Result` object representing
_add_event=_add_event,
)
+ @overload
+ def scalar(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Optional[_T]:
+ ...
+
+ @overload
+ def scalar(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> Any:
+ ...
+
def scalar(
self,
statement: Executable,
**kw,
)
+ @overload
+ def scalars(
+ self,
+ statement: TypedReturnsRows[Tuple[_T]],
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[_T]:
+ ...
+
+ @overload
+ def scalars(
+ self,
+ statement: Executable,
+ params: Optional[_CoreSingleExecuteParams] = None,
+ *,
+ execution_options: _ExecuteOptionsParameter = util.EMPTY_DICT,
+ bind_arguments: Optional[_BindArguments] = None,
+ **kw: Any,
+ ) -> ScalarResult[Any]:
+ ...
+
def scalars(
self,
statement: Executable,
f'{", ".join(context)} or this Session.'
)
+ @overload
+ def query(self, _entity: _EntityType[_O]) -> Query[_O]:
+ ...
+
+ @overload
+ def query(
+ self, _colexpr: TypedColumnsClauseRole[_T]
+ ) -> RowReturningQuery[Tuple[_T]]:
+ ...
+
+ # START OVERLOADED FUNCTIONS self.query RowReturningQuery 2-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def query(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> RowReturningQuery[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def query(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def query(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> RowReturningQuery[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.query
+
+ @overload
+ def query(
+ self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
+ ) -> Query[Any]:
+ ...
+
def query(
- self, *entities: _ColumnsClauseArgument, **kwargs: Any
+ self, *entities: _ColumnsClauseArgument[Any], **kwargs: Any
) -> Query[Any]:
"""Return a new :class:`_query.Query` object corresponding to this
:class:`_orm.Session`.
with_for_update = ForUpdateArg._from_argument(with_for_update)
- stmt = sql.select(object_mapper(instance))
+ stmt: Select[Any] = sql.select(object_mapper(instance))
if (
loading.load_on_ident(
self,
@classmethod
def _instance_level_callable_processor(
cls, manager: ClassManager[_O], fn: _LoaderCallable, key: Any
- ) -> Callable[[InstanceState[_O], _InstanceDict, Row], None]:
+ ) -> Callable[[InstanceState[_O], _InstanceDict, Row[Any]], None]:
impl = manager[key].impl
if is_collection_impl(impl):
fixed_impl = impl
def _set_callable(
- state: InstanceState[_O], dict_: _InstanceDict, row: Row
+ state: InstanceState[_O], dict_: _InstanceDict, row: Row[Any]
) -> None:
if "callables" not in state.__dict__:
state.callables = {}
else:
def _set_callable(
- state: InstanceState[_O], dict_: _InstanceDict, row: Row
+ state: InstanceState[_O], dict_: _InstanceDict, row: Row[Any]
) -> None:
if "callables" not in state.__dict__:
state.callables = {}
import weakref
from . import attributes # noqa
+from . import exc
from ._typing import _O
from ._typing import insp_is_aliased_class
from ._typing import insp_is_mapper
from .base import instance_str as instance_str
from .base import object_mapper as object_mapper
from .base import object_state as object_state
+from .base import opt_manager_of_class
from .base import state_attribute_str as state_attribute_str
from .base import state_class_str as state_class_str
from .base import state_str as state_str
from ..sql.cache_key import HasCacheKey
from ..sql.cache_key import MemoizedHasCacheKey
from ..sql.elements import ColumnElement
+from ..sql.elements import KeyedColumnElement
from ..sql.selectable import FromClause
from ..util.langhelpers import MemoizedSlots
from ..util.typing import de_stringify_annotation
from ..sql.selectable import _ColumnsClauseElement
from ..sql.selectable import Alias
from ..sql.selectable import Subquery
- from ..sql.visitors import _ET
from ..sql.visitors import anon_map
- from ..sql.visitors import ExternallyTraversible
_T = TypeVar("_T", bound=Any)
ident: Union[Any, Tuple[Any, ...]] = None,
*,
instance: Optional[_T] = None,
- row: Optional[Union[Row, RowMapping]] = None,
+ row: Optional[Union[Row[Any], RowMapping]] = None,
identity_token: Optional[Any] = None,
) -> _IdentityKeyType[_T]:
r"""Generate "identity key" tuples, as are used as keys in the
return not entity or entity.isa(self.mapper)
-class AliasedClass(inspection.Inspectable["AliasedInsp[_O]"], Generic[_O]):
+class AliasedClass(
+ inspection.Inspectable["AliasedInsp[_O]"], ORMColumnsClauseRole[_O]
+):
r"""Represents an "aliased" form of a mapped class for usage with Query.
The ORM equivalent of a :func:`~sqlalchemy.sql.expression.alias`
@inspection._self_inspects
class AliasedInsp(
- ORMEntityColumnsClauseRole,
+ ORMEntityColumnsClauseRole[_O],
ORMFromClauseRole,
HasCacheKey,
InspectionAttr,
inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
+@inspection._inspects(type)
+def _inspect_mc(
+ class_: Type[_O],
+) -> Optional[Mapper[_O]]:
+
+ try:
+ class_manager = opt_manager_of_class(class_)
+ if class_manager is None or not class_manager.is_mapped:
+ return None
+ mapper = class_manager.mapper
+ except exc.NO_STATE:
+
+ return None
+ else:
+ return mapper
+
+
@inspection._self_inspects
class Bundle(
- ORMColumnsClauseRole,
+ ORMColumnsClauseRole[_T],
SupportsCloneAnnotations,
MemoizedHasCacheKey,
- inspection.Inspectable["Bundle"],
+ inspection.Inspectable["Bundle[_T]"],
InspectionAttr,
):
"""A grouping of SQL expressions that are returned by a :class:`.Query`
@property
def entity_namespace(
self,
- ) -> ReadOnlyColumnCollection[str, ColumnElement[Any]]:
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
return self.c
- columns: ReadOnlyColumnCollection[str, ColumnElement[Any]]
+ columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
"""A namespace of SQL expressions referred to by this :class:`.Bundle`.
"""
- c: ReadOnlyColumnCollection[str, ColumnElement[Any]]
+ c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
"""An alias for :attr:`.Bundle.columns`."""
def _clone(self):
raw_annotation: Union[type, str],
cls: type,
key: str,
- attr_cls: type,
+ attr_cls: Type[Any],
required: bool,
is_dataclass_field: bool,
+ superclasses: Optional[Tuple[Type[Any], ...]] = None,
) -> Optional[Union[type, str]]:
if raw_annotation is None:
if is_dataclass_field:
return annotated
else:
- if (
- not hasattr(annotated, "__origin__")
- or not issubclass(annotated.__origin__, attr_cls) # type: ignore
+ # TODO: there don't seem to be tests for the failure
+ # conditions here
+ if not hasattr(annotated, "__origin__") or (
+ not issubclass(
+ annotated.__origin__, # type: ignore
+ superclasses if superclasses else attr_cls,
+ )
and not issubclass(attr_cls, annotated.__origin__) # type: ignore
):
our_annotated_str = (
coercions.lambdas = lambdas
coercions.schema = schema
coercions.selectable = selectable
- coercions.traversals = traversals
from .annotation import _prepare_annotations
from .annotation import Annotated
from typing import Any
from typing import Optional
+from typing import overload
+from typing import Tuple
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from . import coercions
from . import roles
from ._typing import _ColumnsClauseArgument
+from ._typing import _no_kw
from .elements import ColumnClause
from .selectable import Alias
from .selectable import CompoundSelect
from ._typing import _FromClauseArgument
from ._typing import _OnClauseArgument
from ._typing import _SelectStatementForCompoundArgument
+ from ._typing import _T0
+ from ._typing import _T1
+ from ._typing import _T2
+ from ._typing import _T3
+ from ._typing import _T4
+ from ._typing import _T5
+ from ._typing import _T6
+ from ._typing import _T7
+ from ._typing import _T8
+ from ._typing import _T9
+ from ._typing import _TypedColumnClauseArgument as _TCCA
from .functions import Function
from .selectable import CTE
from .selectable import HasCTE
from .selectable import SelectBase
+_T = TypeVar("_T", bound=Any)
+
+
def alias(
selectable: FromClause, name: Optional[str] = None, flat: bool = False
) -> NamedFromClause:
)
-def except_(*selects: _SelectStatementForCompoundArgument) -> CompoundSelect:
+def except_(
+ *selects: _SelectStatementForCompoundArgument,
+) -> CompoundSelect:
r"""Return an ``EXCEPT`` of multiple selectables.
The returned object is an instance of
def exists(
__argument: Optional[
- Union[_ColumnsClauseArgument, SelectBase, ScalarSelect[bool]]
+ Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]]
] = None,
) -> Exists:
"""Construct a new :class:`_expression.Exists` construct.
return Exists(__argument)
-def intersect(*selects: _SelectStatementForCompoundArgument) -> CompoundSelect:
+def intersect(
+ *selects: _SelectStatementForCompoundArgument,
+) -> CompoundSelect:
r"""Return an ``INTERSECT`` of multiple selectables.
The returned object is an instance of
return Join(left, right, onclause, isouter=True, full=full)
-def select(*entities: _ColumnsClauseArgument) -> Select:
+# START OVERLOADED FUNCTIONS select Select 1-10
+
+# code within this block is **programmatically,
+# statically generated** by tools/generate_tuple_map_overloads.py
+
+
+@overload
+def select(__ent0: _TCCA[_T0]) -> Select[Tuple[_T0]]:
+ ...
+
+
+@overload
+def select(__ent0: _TCCA[_T0], __ent1: _TCCA[_T1]) -> Select[Tuple[_T0, _T1]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+) -> Select[Tuple[_T0, _T1, _T2]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+) -> Select[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ __ent8: _TCCA[_T8],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8]]:
+ ...
+
+
+@overload
+def select(
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ __ent8: _TCCA[_T8],
+ __ent9: _TCCA[_T9],
+) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7, _T8, _T9]]:
+ ...
+
+
+# END OVERLOADED FUNCTIONS select
+
+
+@overload
+def select(*entities: _ColumnsClauseArgument[Any], **__kw: Any) -> Select[Any]:
+ ...
+
+
+def select(*entities: _ColumnsClauseArgument[Any], **__kw: Any) -> Select[Any]:
r"""Construct a new :class:`_expression.Select`.
given, as well as ORM-mapped classes.
"""
-
+ # the keyword args are a necessary element in order for the typing
+ # to work out w/ the varargs vs. having named "keyword" arguments that
+ # aren't always present.
+ if __kw:
+ raise _no_kw()
return Select(*entities)
return TableSample._factory(selectable, sampling, name=name, seed=seed)
-def union(*selects: _SelectStatementForCompoundArgument) -> CompoundSelect:
+def union(
+ *selects: _SelectStatementForCompoundArgument,
+) -> CompoundSelect:
r"""Return a ``UNION`` of multiple selectables.
The returned object is an instance of
return CompoundSelect._create_union(*selects)
-def union_all(*selects: _SelectStatementForCompoundArgument) -> CompoundSelect:
+def union_all(
+ *selects: _SelectStatementForCompoundArgument,
+) -> CompoundSelect:
r"""Return a ``UNION ALL`` of multiple selectables.
The returned object is an instance of
from typing import Callable
from typing import Dict
from typing import Set
+from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
from . import roles
+from .. import exc
from .. import util
from ..inspection import Inspectable
from ..util.typing import Literal
from ..util.typing import Protocol
if TYPE_CHECKING:
+ from datetime import date
+ from datetime import datetime
+ from datetime import time
+ from datetime import timedelta
+ from decimal import Decimal
+ from uuid import UUID
+
from .base import Executable
from .compiler import Compiled
from .compiler import DDLCompiler
from .elements import ClauseElement
from .elements import ColumnClause
from .elements import ColumnElement
+ from .elements import KeyedColumnElement
from .elements import quoted_name
- from .elements import SQLCoreOperations
from .elements import TextClause
from .lambdas import LambdaElement
from .roles import ColumnsClauseRole
from .roles import FromClauseRole
from .schema import Column
- from .schema import DefaultGenerator
- from .schema import Sequence
- from .schema import Table
from .selectable import Alias
+ from .selectable import CTE
from .selectable import FromClause
from .selectable import Join
from .selectable import NamedFromClause
...
+# match column types that are not ORM entities
+_NOT_ENTITY = TypeVar(
+ "_NOT_ENTITY",
+ int,
+ str,
+ "datetime",
+ "date",
+ "time",
+ "timedelta",
+ "UUID",
+ float,
+ "Decimal",
+)
+
+_MAYBE_ENTITY = TypeVar(
+ "_MAYBE_ENTITY",
+ roles.ColumnsClauseRole,
+ Literal["*", 1],
+ Type[Any],
+ Inspectable[_HasClauseElement],
+ _HasClauseElement,
+)
+
+
# convention:
# XYZArgument - something that the end user is passing to a public API method
# XYZElement - the internal representation that we use for the thing.
]
_ColumnsClauseArgument = Union[
- Literal["*", 1],
+ roles.TypedColumnsClauseRole[_T],
roles.ColumnsClauseRole,
- Type[Any],
+ Literal["*", 1],
+ Type[_T],
Inspectable[_HasClauseElement],
_HasClauseElement,
]
"""
+_TypedColumnClauseArgument = Union[
+ roles.TypedColumnsClauseRole[_T], roles.ExpressionElementRole[_T], Type[_T]
+]
+
+_TP = TypeVar("_TP", bound=Tuple[Any, ...])
+
+_T0 = TypeVar("_T0", bound=Any)
+_T1 = TypeVar("_T1", bound=Any)
+_T2 = TypeVar("_T2", bound=Any)
+_T3 = TypeVar("_T3", bound=Any)
+_T4 = TypeVar("_T4", bound=Any)
+_T5 = TypeVar("_T5", bound=Any)
+_T6 = TypeVar("_T6", bound=Any)
+_T7 = TypeVar("_T7", bound=Any)
+_T8 = TypeVar("_T8", bound=Any)
+_T9 = TypeVar("_T9", bound=Any)
+
+
_ColumnExpressionArgument = Union[
"ColumnElement[_T]",
_HasClauseElement,
"TableClause",
"Join",
"Alias",
+ "CTE",
Type[Any],
Inspectable[_HasClauseElement],
_HasClauseElement,
def is_column_element(c: ClauseElement) -> TypeGuard[ColumnElement[Any]]:
...
+ def is_keyed_column_element(
+ c: ClauseElement,
+ ) -> TypeGuard[KeyedColumnElement[Any]]:
+ ...
+
def is_text_clause(c: ClauseElement) -> TypeGuard[TextClause]:
...
def is_select_statement(
t: Union[Executable, ReturnsRows]
- ) -> TypeGuard[Select]:
+ ) -> TypeGuard[Select[Any]]:
...
def is_table(t: FromClause) -> TypeGuard[TableClause]:
is_ddl_compiler = operator.attrgetter("is_ddl")
is_named_from_clause = operator.attrgetter("named_with_column")
is_column_element = operator.attrgetter("_is_column_element")
+ is_keyed_column_element = operator.attrgetter("_is_keyed_column_element")
is_text_clause = operator.attrgetter("_is_text_clause")
is_from_clause = operator.attrgetter("_is_from_clause")
is_tuple_type = operator.attrgetter("_is_tuple_type")
def is_insert_update(c: ClauseElement) -> TypeGuard[ValuesBase]:
return c.is_dml and (c.is_insert or c.is_update) # type: ignore
+
+
+def _no_kw() -> exc.ArgumentError:
+ return exc.ArgumentError(
+ "Additional keyword arguments are not accepted by this "
+ "function/method. The presence of **kw is for pep-484 typing purposes"
+ )
from . import coercions
from . import elements
from . import type_api
- from ._typing import _ColumnsClauseArgument
from .elements import BindParameter
- from .elements import ColumnClause
+ from .elements import ColumnClause # noqa
from .elements import ColumnElement
+ from .elements import KeyedColumnElement
from .elements import NamedColumn
from .elements import SQLCoreOperations
from .elements import TextClause
from .selectable import FromClause
from ..engine import Connection
from ..engine import CursorResult
- from ..engine import Result
from ..engine.base import _CompiledCacheType
from ..engine.interfaces import _CoreMultiExecuteParams
from ..engine.interfaces import _ExecuteOptions
"""Provide a method-chaining pattern in conjunction with the
@_generative decorator that mutates in place."""
+ __slots__ = ()
+
def _generate(self):
skip = self._memoized_keys
+ # note __dict__ needs to be in __slots__ if this is used
for k in skip:
self.__dict__.pop(k, None)
return self
SelfExecutable = TypeVar("SelfExecutable", bound="Executable")
-class Executable(roles.StatementRole, Generative):
+class Executable(roles.StatementRole):
"""Mark a :class:`_expression.ClauseElement` as supporting execution.
:class:`.Executable` is a superclass for all "statement" types
connection: Connection,
distilled_params: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
...
def _execute_on_scalar(
_COLKEY = TypeVar("_COLKEY", Union[None, str], str)
_COL_co = TypeVar("_COL_co", bound="ColumnElement[Any]", covariant=True)
-_COL = TypeVar("_COL", bound="ColumnElement[Any]")
+_COL = TypeVar("_COL", bound="KeyedColumnElement[Any]")
class ColumnCollection(Generic[_COLKEY, _COL_co]):
) -> None:
"""populate from an iterator of (key, column)"""
cols = list(iter_)
+
self._collection[:] = cols
self._colset.update(c for k, c in self._collection)
self._index.update(
from . import operators
from . import roles
from . import visitors
+from ._typing import is_from_clause
from .base import ExecutableOption
from .base import Options
from .cache_key import HasCacheKey
from .. import util
from ..util.typing import Literal
-if not typing.TYPE_CHECKING:
- elements = None
- lambdas = None
- schema = None
- selectable = None
- traversals = None
-
if typing.TYPE_CHECKING:
from . import elements
from . import lambdas
from . import schema
from . import selectable
- from . import traversals
from ._typing import _ColumnExpressionArgument
from ._typing import _ColumnsClauseArgument
from ._typing import _DDLColumnArgument
from ._typing import _DMLTableArgument
from ._typing import _FromClauseArgument
from .dml import _DMLTableElement
+ from .elements import BindParameter
from .elements import ClauseElement
from .elements import ColumnClause
from .elements import ColumnElement
from .elements import SQLCoreOperations
from .schema import Column
from .selectable import _ColumnsClauseElement
- from .selectable import _JoinTargetElement
from .selectable import _JoinTargetProtocol
- from .selectable import _OnClauseElement
from .selectable import FromClause
from .selectable import HasCTE
from .selectable import SelectBase
...
+@overload
+def expect(
+ role: Type[roles.LiteralValueRole],
+ element: Any,
+ **kw: Any,
+) -> BindParameter[Any]:
+ ...
+
+
@overload
def expect(
role: Type[roles.DDLReferredColumnRole],
@overload
def expect(
role: Type[roles.ColumnsClauseRole],
- element: _ColumnsClauseArgument,
+ element: _ColumnsClauseArgument[Any],
**kw: Any,
) -> _ColumnsClauseElement:
...
argname: Optional[str] = None,
**kw: Any,
) -> Any:
- if isinstance(resolved, roles.StrictFromClauseRole):
+ if is_from_clause(resolved):
return elements.ClauseList(*resolved.c)
else:
return resolved
from ..util.typing import TypedDict
if typing.TYPE_CHECKING:
- from . import roles
from .annotation import _AnnotationDict
from .base import _AmbiguousTableNameMap
from .base import CompileState
from .elements import ColumnElement
from .elements import Label
from .functions import Function
- from .selectable import Alias
from .selectable import AliasedReturnsRows
from .selectable import CompoundSelectState
from .selectable import CTE
need_result_map_for_nested: bool
need_result_map_for_compound: bool
select_0: ReturnsRows
- insert_from_select: Select
+ insert_from_select: Select[Any]
class ExpandedState(NamedTuple):
"unique bind parameter of the same name" % name
)
elif existing._is_crud or bindparam._is_crud:
- raise exc.CompileError(
- "bindparam() name '%s' is reserved "
- "for automatic usage in the VALUES or SET "
- "clause of this "
- "insert/update statement. Please use a "
- "name other than column name when using bindparam() "
- "with insert() or update() (for example, 'b_%s')."
- % (bindparam.key, bindparam.key)
- )
+ if existing._is_crud and bindparam._is_crud:
+ # TODO: this condition is not well understood.
+ # see tests in test/sql/test_update.py
+ raise exc.CompileError(
+ "Encountered unsupported case when compiling an "
+ "INSERT or UPDATE statement. If this is a "
+ "multi-table "
+ "UPDATE statement, please provide string-named "
+ "arguments to the "
+ "values() method with distinct names; support for "
+ "multi-table UPDATE statements that "
+ "target multiple tables for UPDATE is very "
+ "limited",
+ )
+ else:
+ raise exc.CompileError(
+ f"bindparam() name '{bindparam.key}' is reserved "
+ "for automatic usage in the VALUES or SET "
+ "clause of this "
+ "insert/update statement. Please use a "
+ "name other than column name when using "
+ "bindparam() "
+ "with insert() or update() (for example, "
+ f"'b_{bindparam.key}')."
+ )
self.binds[bindparam.key] = self.binds[name] = bindparam
return text
def _setup_select_hints(
- self, select: Select
+ self, select: Select[Any]
) -> Tuple[str, _FromHintsType]:
byfrom = dict(
[
from typing import NamedTuple
from typing import Optional
from typing import overload
+from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
from . import dml
from . import elements
from . import roles
+from .elements import ColumnClause
from .schema import default_is_clause_element
from .schema import default_is_sequence
+from .selectable import TableClause
from .. import exc
from .. import util
from ..util.typing import Literal
from .compiler import SQLCompiler
from .dml import _DMLColumnElement
from .dml import DMLState
- from .dml import Insert
- from .dml import Update
- from .dml import UpdateDMLState
from .dml import ValuesBase
- from .elements import ClauseElement
- from .elements import ColumnClause
from .elements import ColumnElement
- from .elements import TextClause
from .schema import _SQLExprDefault
- from .schema import Column
from .selectable import TableClause
REQUIRED = util.symbol(
)
+def _as_dml_column(c: ColumnElement[Any]) -> ColumnClause[Any]:
+ if not isinstance(c, ColumnClause):
+ raise exc.CompileError(
+ f"Can't create DML statement against column expression {c!r}"
+ )
+ return c
+
+
class _CrudParams(NamedTuple):
- single_params: List[
- Tuple[ColumnClause[Any], str, Optional[Union[str, _SQLExprDefault]]]
+ single_params: Sequence[
+ Tuple[ColumnElement[Any], str, Optional[Union[str, _SQLExprDefault]]]
]
all_multi_params: List[
- List[
+ Sequence[
Tuple[
ColumnClause[Any],
str,
compiler,
stmt,
compile_state,
- cast("List[Tuple[ColumnClause[Any], str, str]]", values),
+ cast("Sequence[Tuple[ColumnClause[Any], str, str]]", values),
cast("Callable[..., str]", _column_as_key),
kw,
)
# insert_executemany_returning mode :)
values = [
(
- stmt.table.columns[0],
+ _as_dml_column(stmt.table.columns[0]),
compiler.preparer.format_column(stmt.table.columns[0]),
"DEFAULT",
)
compiler: SQLCompiler,
stmt: ValuesBase,
compile_state: DMLState,
- initial_values: List[Tuple[ColumnClause[Any], str, str]],
+ initial_values: Sequence[Tuple[ColumnClause[Any], str, str]],
_column_as_key: Callable[..., str],
kw: Dict[str, Any],
-) -> List[List[Tuple[ColumnClause[Any], str, str]]]:
+) -> List[Sequence[Tuple[ColumnClause[Any], str, str]]]:
values_0 = initial_values
values = [initial_values]
from typing import MutableMapping
from typing import NoReturn
from typing import Optional
+from typing import overload
from typing import Sequence
from typing import Tuple
from typing import Type
from typing import TYPE_CHECKING
+from typing import TypeVar
from typing import Union
from . import coercions
from . import roles
from . import util as sql_util
+from ._typing import _no_kw
+from ._typing import _TP
from ._typing import is_column_element
from ._typing import is_named_from_clause
from .base import _entity_namespace_key
from .base import CompileState
from .base import DialectKWArgs
from .base import Executable
+from .base import Generative
from .base import HasCompileState
from .elements import BooleanClauseList
from .elements import ClauseElement
from .elements import ColumnElement
from .elements import Null
from .selectable import Alias
+from .selectable import ExecutableReturnsRows
from .selectable import FromClause
from .selectable import HasCTE
from .selectable import HasPrefixes
from .selectable import Join
-from .selectable import ReturnsRows
from .selectable import TableClause
+from .selectable import TypedReturnsRows
from .sqltypes import NullType
from .visitors import InternalTraversal
from .. import exc
from ._typing import _ColumnsClauseArgument
from ._typing import _DMLColumnArgument
from ._typing import _DMLTableArgument
- from ._typing import _FromClauseArgument
+ from ._typing import _T0 # noqa
+ from ._typing import _T1 # noqa
+ from ._typing import _T2 # noqa
+ from ._typing import _T3 # noqa
+ from ._typing import _T4 # noqa
+ from ._typing import _T5 # noqa
+ from ._typing import _T6 # noqa
+ from ._typing import _T7 # noqa
+ from ._typing import _TypedColumnClauseArgument as _TCCA # noqa
from .base import ReadOnlyColumnCollection
from .compiler import SQLCompiler
+ from .elements import ColumnElement
+ from .elements import KeyedColumnElement
from .selectable import _ColumnsClauseElement
from .selectable import _SelectIterable
from .selectable import Select
isinsert = operator.attrgetter("isinsert")
+_T = TypeVar("_T", bound=Any)
+
_DMLColumnElement = Union[str, ColumnClause[Any]]
_DMLTableElement = Union[TableClause, Alias, Join]
"%s construct does not support "
"multiple parameter sets." % statement.__visit_name__.upper()
)
+ else:
+ assert isinstance(statement, Insert)
+
+ # which implies...
+ # assert isinstance(statement.table, TableClause)
for parameters in statement._multi_values:
multi_parameters: List[MutableMapping[_DMLColumnElement, Any]] = [
elif statement._multi_values:
self._process_multi_values(statement)
self._extra_froms = ef = self._make_extra_froms(statement)
- self.is_multitable = mt = ef and self._dict_parameters
+
+ self.is_multitable = mt = ef
+
self.include_table_with_column_exprs = bool(
mt and compiler.render_table_with_column_in_update_from
)
HasCompileState,
DialectKWArgs,
HasPrefixes,
- ReturnsRows,
- Executable,
+ Generative,
+ ExecutableReturnsRows,
ClauseElement,
):
"""Form the base for ``INSERT``, ``UPDATE``, and ``DELETE`` statements."""
@_generative
def returning(
- self: SelfUpdateBase, *cols: _ColumnsClauseArgument
- ) -> SelfUpdateBase:
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> UpdateBase:
r"""Add a :term:`RETURNING` or equivalent clause to this statement.
e.g.:
:ref:`tutorial_insert_returning` - in the :ref:`unified_tutorial`
""" # noqa: E501
+ if __kw:
+ raise _no_kw()
if self._return_defaults:
raise exc.InvalidRequestError(
"return_defaults() is already configured on this statement"
return self
def corresponding_column(
- self, column: ColumnElement[Any], require_embedded: bool = False
+ self, column: KeyedColumnElement[Any], require_embedded: bool = False
) -> Optional[ColumnElement[Any]]:
return self.exported_columns.corresponding_column(
column, require_embedded=require_embedded
_supports_multi_parameters = False
- select: Optional[Select] = None
+ select: Optional[Select[Any]] = None
"""SELECT statement for INSERT .. FROM SELECT"""
_post_values_clause: Optional[ClauseElement] = None
)
elif isinstance(arg, collections_abc.Sequence):
-
if arg and isinstance(arg[0], (list, dict, tuple)):
self._multi_values += (arg,)
return self
+ if TYPE_CHECKING:
+ # crud.py raises during compilation if this is not the
+ # case
+ assert isinstance(self, Insert)
+
# tuple values
arg = {c.key: value for c, value in zip(self.table.c, arg)}
def from_select(
self: SelfInsert,
names: List[str],
- select: Select,
+ select: Select[Any],
include_defaults: bool = True,
) -> SelfInsert:
"""Return a new :class:`_expression.Insert` construct which represents
self.select = coercions.expect(roles.DMLSelectRole, select)
return self
+ if TYPE_CHECKING:
+
+ # START OVERLOADED FUNCTIONS self.returning ReturningInsert 1-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def returning(self, __ent0: _TCCA[_T0]) -> ReturningInsert[Tuple[_T0]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> ReturningInsert[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> ReturningInsert[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.returning
+
+ @overload
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningInsert[Any]:
+ ...
+
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningInsert[Any]:
+ ...
+
+
+class ReturningInsert(Insert, TypedReturnsRows[_TP]):
+ """Typing-only class that establishes a generic type form of
+ :class:`.Insert` which tracks returned column types.
+
+ This datatype is delivered when calling the
+ :meth:`.Insert.returning` method.
+
+ .. versionadded:: 2.0
+
+ """
+
SelfDMLWhereBase = typing.TypeVar("SelfDMLWhereBase", bound="DMLWhereBase")
self._inline = True
return self
+ if TYPE_CHECKING:
+ # START OVERLOADED FUNCTIONS self.returning ReturningUpdate 1-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def returning(self, __ent0: _TCCA[_T0]) -> ReturningUpdate[Tuple[_T0]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> ReturningUpdate[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> ReturningUpdate[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.returning
+
+ @overload
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningUpdate[Any]:
+ ...
+
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningUpdate[Any]:
+ ...
+
+
+class ReturningUpdate(Update, TypedReturnsRows[_TP]):
+ """Typing-only class that establishes a generic type form of
+ :class:`.Update` which tracks returned column types.
+
+ This datatype is delivered when calling the
+ :meth:`.Update.returning` method.
+
+ .. versionadded:: 2.0
+
+ """
+
SelfDelete = typing.TypeVar("SelfDelete", bound="Delete")
self.table = coercions.expect(
roles.DMLTableRole, table, apply_propagate_attrs=self
)
+
+ if TYPE_CHECKING:
+
+ # START OVERLOADED FUNCTIONS self.returning ReturningDelete 1-8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_tuple_map_overloads.py
+
+ @overload
+ def returning(self, __ent0: _TCCA[_T0]) -> ReturningDelete[Tuple[_T0]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> ReturningDelete[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def returning(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def returning(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> ReturningDelete[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.returning
+
+ @overload
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningDelete[Any]:
+ ...
+
+ def returning(
+ self, *cols: _ColumnsClauseArgument[Any], **__kw: Any
+ ) -> ReturningDelete[Any]:
+ ...
+
+
+class ReturningDelete(Update, TypedReturnsRows[_TP]):
+ """Typing-only class that establishes a generic type form of
+ :class:`.Delete` which tracks returned column types.
+
+ This datatype is delivered when calling the
+ :meth:`.Delete.returning` method.
+
+ .. versionadded:: 2.0
+
+ """
from .base import _generative
from .base import _NoArg
from .base import Executable
+from .base import Generative
from .base import HasMemoized
from .base import Immutable
from .base import NO_ARG
from .selectable import _SelectIterable
from .selectable import FromClause
from .selectable import NamedFromClause
- from .selectable import ReturnsRows
from .selectable import Select
- from .selectable import TableClause
- from .sqltypes import Boolean
from .sqltypes import TupleType
from .type_api import TypeEngine
from .visitors import _CloneCallableType
_NMT = TypeVar("_NMT", bound="_NUMBER")
-def literal(value, type_=None):
+def literal(
+ value: Any, type_: Optional[_TypeEngineArgument[_T]] = None
+) -> BindParameter[_T]:
r"""Return a literal clause, bound to a bind parameter.
Literal clauses are created automatically when non-
return coercions.expect(roles.LiteralValueRole, value, type_=type_)
-def literal_column(text, type_=None):
+def literal_column(
+ text: str, type_: Optional[_TypeEngineArgument[_T]] = None
+) -> ColumnClause[_T]:
r"""Produce a :class:`.ColumnClause` object that has the
:paramref:`_expression.column.is_literal` flag set to True.
is_selectable = False
is_dml = False
_is_column_element = False
+ _is_keyed_column_element = False
_is_table = False
_is_textual = False
_is_from_clause = False
if typing.TYPE_CHECKING:
def get_children(
- self, omit_attrs: typing_Tuple[str, ...] = ..., **kw: Any
+ self, *, omit_attrs: typing_Tuple[str, ...] = ..., **kw: Any
) -> Iterable[ClauseElement]:
...
connection: Connection,
distilled_params: _CoreMultiExecuteParams,
execution_options: _ExecuteOptions,
- ) -> Result:
+ ) -> Result[Any]:
if self.supports_execution:
if TYPE_CHECKING:
assert isinstance(self, Executable)
def in_(
self,
- other: Union[Sequence[Any], BindParameter[Any], Select],
+ other: Union[Sequence[Any], BindParameter[Any], Select[Any]],
) -> BinaryExpression[bool]:
...
def not_in(
self,
- other: Union[Sequence[Any], BindParameter[Any], Select],
+ other: Union[Sequence[Any], BindParameter[Any], Select[Any]],
) -> BinaryExpression[bool]:
...
return self._anon_label(label, add_hash=idx)
+class KeyedColumnElement(ColumnElement[_T]):
+ """ColumnElement where ``.key`` is non-None."""
+
+ _is_keyed_column_element = True
+
+ key: str
+
+
class WrapsColumnExpression(ColumnElement[_T]):
"""Mixin that defines a :class:`_expression.ColumnElement`
as a wrapper with special
SelfBindParameter = TypeVar("SelfBindParameter", bound="BindParameter[Any]")
-class BindParameter(roles.InElementRole, ColumnElement[_T]):
+class BindParameter(roles.InElementRole, KeyedColumnElement[_T]):
r"""Represent a "bound expression".
:class:`.BindParameter` is invoked explicitly using the
roles.FromClauseRole,
roles.SelectStatementRole,
roles.InElementRole,
+ Generative,
Executable,
DQLDMLClauseElement,
roles.BinaryElementRole[Any],
)
-class NamedColumn(ColumnElement[_T]):
+class NamedColumn(KeyedColumnElement[_T]):
is_literal = False
table: Optional[FromClause] = None
name: str
self.is_literal = is_literal
- def get_children(self, column_tables=False, **kw):
+ def get_children(self, *, column_tables=False, **kw):
# override base get_children() to not return the Table
# or selectable that is parent to this column. Traversals
# expect the columns of tables and subqueries to be leaf nodes.
connection: Connection,
distilled_params: _CoreMultiExecuteParams,
execution_options: _ExecuteOptionsParameter,
- ) -> CursorResult:
+ ) -> CursorResult[Any]:
return connection._execute_function(
self, distilled_params, execution_options
)
joins_implicitly=joins_implicitly,
)
- def select(self) -> "Select":
+ def select(self) -> Select[Any]:
"""Produce a :func:`_expression.select` construct
against this :class:`.FunctionElement`.
s = select(function_element)
"""
- s = Select(self)
+ s: Select[Any] = Select(self)
if self._execution_options:
s = s.execution_options(**self._execution_options)
return s
@overload
def __call__(
- self, *c: Any, type_: TypeEngine[_T], **kwargs: Any
+ self, *c: Any, type_: _TypeEngineArgument[_T], **kwargs: Any
) -> Function[_T]:
...
from typing import Any
from typing import Generic
-from typing import Iterable
-from typing import List
from typing import Optional
from typing import TYPE_CHECKING
from typing import TypeVar
if TYPE_CHECKING:
from ._typing import _PropagateAttrsType
- from .base import _EntityNamespace
- from .base import ColumnCollection
- from .base import ReadOnlyColumnCollection
- from .elements import ColumnClause
from .elements import Label
- from .elements import NamedColumn
from .selectable import _SelectIterable
from .selectable import FromClause
from .selectable import Subquery
class ColumnsClauseRole(AllowsLambdaRole, UsesInspection, ColumnListRole):
__slots__ = ()
- _role_name = "Column expression or FROM clause"
+ _role_name = (
+ "Column expression, FROM clause, or other columns clause element"
+ )
@property
def _select_iterable(self) -> _SelectIterable:
raise NotImplementedError()
+class TypedColumnsClauseRole(Generic[_T], SQLRole):
+ """element-typed form of ColumnsClauseRole"""
+
+ __slots__ = ()
+
+
class LimitOffsetRole(SQLRole):
__slots__ = ()
_role_name = "LIMIT / OFFSET expression"
_role_name = "SQL expression for WHERE/HAVING role"
-class ExpressionElementRole(Generic[_T], SQLRole):
+class ExpressionElementRole(TypedColumnsClauseRole[_T]):
# note when using generics for ExpressionElementRole,
# the generic type needs to be in
# sqlalchemy.sql.coercions._impl_lookup mapping also.
named_with_column: bool
- if TYPE_CHECKING:
-
- @util.ro_non_memoized_property
- def c(self) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]:
- ...
-
- @util.ro_non_memoized_property
- def columns(self) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]:
- ...
-
- @util.ro_non_memoized_property
- def entity_namespace(self) -> _EntityNamespace:
- ...
-
- @util.ro_non_memoized_property
- def _hide_froms(self) -> Iterable[FromClause]:
- ...
-
- @util.ro_non_memoized_property
- def _from_objects(self) -> List[FromClause]:
- ...
-
class StrictFromClauseRole(FromClauseRole):
__slots__ = ()
# does not allow text() or select() objects
- if TYPE_CHECKING:
-
- @util.ro_non_memoized_property
- def description(self) -> str:
- ...
-
class AnonymizedFromClauseRole(StrictFromClauseRole):
__slots__ = ()
__slots__ = ()
_role_name = "subject table for an INSERT, UPDATE or DELETE"
- if TYPE_CHECKING:
-
- @util.ro_non_memoized_property
- def primary_key(self) -> Iterable[NamedColumn[Any]]:
- ...
-
- @util.ro_non_memoized_property
- def columns(self) -> ReadOnlyColumnCollection[str, ColumnClause[Any]]:
- ...
-
class DMLColumnRole(SQLRole):
__slots__ = ()
from ._typing import _InfoType
from ._typing import _TextCoercedExpressionArgument
from ._typing import _TypeEngineArgument
- from .base import ColumnCollection
from .base import DedupeColumnCollection
from .base import ReadOnlyColumnCollection
from .compiler import DDLCompiler
from .visitors import anon_map
from ..engine import Connection
from ..engine import Engine
- from ..engine.cursor import CursorResult
from ..engine.interfaces import _CoreMultiExecuteParams
- from ..engine.interfaces import _CoreSingleExecuteParams
from ..engine.interfaces import _ExecuteOptionsParameter
from ..engine.interfaces import ExecutionContext
from ..engine.mock import MockConnection
:class:`_schema.Table`.
"""
-
- return table.columns.corresponding_column(self.column)
+ # our column is a Column, and any subquery etc. proxying us
+ # would be doing so via another Column, so that's what would
+ # be returned here
+ return table.columns.corresponding_column(self.column) # type: ignore
@util.memoized_property
def _column_tokens(self) -> Tuple[Optional[str], str, Optional[str]]:
from typing import Callable
from typing import cast
from typing import Dict
+from typing import Generic
from typing import Iterable
from typing import Iterator
from typing import List
from . import type_api
from . import visitors
from ._typing import _ColumnsClauseArgument
+from ._typing import _no_kw
+from ._typing import _TP
from ._typing import is_column_element
from ._typing import is_select_statement
from ._typing import is_subquery
from ._typing import _ColumnExpressionArgument
from ._typing import _FromClauseArgument
from ._typing import _JoinTargetArgument
+ from ._typing import _MAYBE_ENTITY
+ from ._typing import _NOT_ENTITY
from ._typing import _OnClauseArgument
from ._typing import _SelectStatementForCompoundArgument
+ from ._typing import _T0
+ from ._typing import _T1
+ from ._typing import _T2
+ from ._typing import _T3
+ from ._typing import _T4
+ from ._typing import _T5
+ from ._typing import _T6
+ from ._typing import _T7
from ._typing import _TextCoercedExpressionArgument
+ from ._typing import _TypedColumnClauseArgument as _TCCA
from ._typing import _TypeEngineArgument
from .base import _AmbiguousTableNameMap
from .base import ExecutableOption
from .dml import Delete
from .dml import Insert
from .dml import Update
+ from .elements import KeyedColumnElement
from .elements import NamedColumn
from .elements import TextClause
from .functions import Function
- from .schema import Column
from .schema import ForeignKey
from .schema import ForeignKeyConstraint
from .type_api import TypeEngine
- from .util import ClauseAdapter
from .visitors import _CloneCallableType
raise NotImplementedError()
+class ExecutableReturnsRows(Executable, ReturnsRows):
+ """base for executable statements that return rows."""
+
+
+class TypedReturnsRows(ExecutableReturnsRows, Generic[_TP]):
+ """base for executable statements that return rows."""
+
+
SelfSelectable = TypeVar("SelfSelectable", bound="Selectable")
)
def corresponding_column(
- self, column: ColumnElement[Any], require_embedded: bool = False
- ) -> Optional[ColumnElement[Any]]:
+ self, column: KeyedColumnElement[Any], require_embedded: bool = False
+ ) -> Optional[KeyedColumnElement[Any]]:
"""Given a :class:`_expression.ColumnElement`, return the exported
:class:`_expression.ColumnElement` object from the
:attr:`_expression.Selectable.exported_columns`
_use_schema_map = False
- def select(self) -> Select:
+ def select(self) -> Select[Any]:
r"""Return a SELECT of this :class:`_expression.FromClause`.
)
@util.ro_non_memoized_property
- def exported_columns(self) -> ReadOnlyColumnCollection[str, Any]:
+ def exported_columns(
+ self,
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
that represents the "exported"
columns of this :class:`_expression.Selectable`.
return self.c
@util.ro_non_memoized_property
- def columns(self) -> ReadOnlyColumnCollection[str, Any]:
+ def columns(
+ self,
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
"""A named-based collection of :class:`_expression.ColumnElement`
objects maintained by this :class:`_expression.FromClause`.
return self.c
@util.ro_memoized_property
- def c(self) -> ReadOnlyColumnCollection[str, Any]:
+ def c(self) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
"""
A synonym for :attr:`.FromClause.columns`
@util.preload_module("sqlalchemy.sql.util")
def _populate_column_collection(self):
sqlutil = util.preloaded.sql_util
- columns: List[ColumnClause[Any]] = [c for c in self.left.c] + [
+ columns: List[KeyedColumnElement[Any]] = [c for c in self.left.c] + [
c for c in self.right.c
]
"join explicitly." % (a.description, b.description)
)
- def select(self) -> "Select":
+ def select(self) -> Select[Any]:
r"""Create a :class:`_expression.Select` from this
:class:`_expression.Join`.
cls, selectable: SelectBase, name: Optional[str] = None
) -> Subquery:
"""Return a :class:`.Subquery` object."""
+
return coercions.expect(
roles.SelectStatementRole, selectable
).subquery(name=name)
roles.CompoundElementRole,
roles.InElementRole,
HasCTE,
- Executable,
SupportsCloneAnnotations,
Selectable,
):
self._reset_memoizations()
@util.ro_non_memoized_property
- def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]:
+ def selected_columns(
+ self,
+ ) -> ColumnCollection[str, ColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
representing the columns that
this SELECT statement or similar construct returns in its result set.
raise NotImplementedError()
@property
- def exported_columns(self) -> ReadOnlyColumnCollection[str, Any]:
+ def exported_columns(
+ self,
+ ) -> ReadOnlyColumnCollection[str, ColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
that represents the "exported"
columns of this :class:`_expression.Selectable`, not including
def as_scalar(self):
return self.scalar_subquery()
- def exists(self):
+ def exists(self) -> Exists:
"""Return an :class:`_sql.Exists` representation of this selectable,
which can be used as a column expression.
"""
return Exists(self)
- def scalar_subquery(self):
+ def scalar_subquery(self) -> ScalarSelect[Any]:
"""Return a 'scalar' representation of this selectable, which can be
used as a column expression.
)
-class GenerativeSelect(SelectBase):
+class GenerativeSelect(SelectBase, Generative):
"""Base class for SELECT statements where additional elements can be
added.
INTERSECT_ALL = "INTERSECT ALL"
-class CompoundSelect(HasCompileState, GenerativeSelect):
+class CompoundSelect(HasCompileState, GenerativeSelect, ExecutableReturnsRows):
"""Forms the basis of ``UNION``, ``UNION ALL``, and other
SELECT-based set operations.
return self.selects[0]._all_selected_columns
@util.ro_non_memoized_property
- def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]:
+ def selected_columns(
+ self,
+ ) -> ColumnCollection[str, ColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
representing the columns that
this SELECT statement or similar construct returns in its result set,
...
def __init__(
- self, statement: Select, compiler: Optional[SQLCompiler], **kw: Any
+ self,
+ statement: Select[Any],
+ compiler: Optional[SQLCompiler],
+ **kw: Any,
):
self.statement = statement
self.from_clauses = statement._from_obj
@classmethod
def get_column_descriptions(
- cls, statement: Select
+ cls, statement: Select[Any]
) -> List[Dict[str, Any]]:
return [
{
@classmethod
def from_statement(
- cls, statement: Select, from_statement: ReturnsRows
- ) -> Any:
+ cls, statement: Select[Any], from_statement: ExecutableReturnsRows
+ ) -> ExecutableReturnsRows:
cls._plugin_not_implemented()
@classmethod
- def get_columns_clause_froms(cls, statement: Select) -> List[FromClause]:
+ def get_columns_clause_froms(
+ cls, statement: Select[Any]
+ ) -> List[FromClause]:
return cls._normalize_froms(
itertools.chain.from_iterable(
element._from_objects for element in statement._raw_columns
return go
- def _get_froms(self, statement: Select) -> List[FromClause]:
+ def _get_froms(self, statement: Select[Any]) -> List[FromClause]:
ambiguous_table_name_map: _AmbiguousTableNameMap
self._ambiguous_table_name_map = ambiguous_table_name_map = {}
def _normalize_froms(
cls,
iterable_of_froms: Iterable[FromClause],
- check_statement: Optional[Select] = None,
+ check_statement: Optional[Select[Any]] = None,
ambiguous_table_name_map: Optional[_AmbiguousTableNameMap] = None,
) -> List[FromClause]:
"""given an iterable of things to select FROM, reduce them to what
@classmethod
def determine_last_joined_entity(
- cls, stmt: Select
+ cls, stmt: Select[Any]
) -> Optional[_JoinTargetElement]:
if stmt._setup_joins:
return stmt._setup_joins[-1][0]
return None
@classmethod
- def all_selected_columns(cls, statement: Select) -> _SelectIterable:
+ def all_selected_columns(cls, statement: Select[Any]) -> _SelectIterable:
return [c for c in _select_iterables(statement._raw_columns)]
def _setup_joins(
return c # type: ignore
@classmethod
- def _generate_for_statement(cls, select_stmt: Select) -> None:
+ def _generate_for_statement(cls, select_stmt: Select[Any]) -> None:
if select_stmt._setup_joins or select_stmt._with_options:
self = _MemoizedSelectEntities()
self._raw_columns = select_stmt._raw_columns
select_stmt._setup_joins = select_stmt._with_options = ()
-SelfSelect = typing.TypeVar("SelfSelect", bound="Select")
+SelfSelect = typing.TypeVar("SelfSelect", bound="Select[Any]")
class Select(
HasCompileState,
_SelectFromElements,
GenerativeSelect,
+ TypedReturnsRows[_TP],
):
"""Represents a ``SELECT`` statement.
_compile_state_factory: Type[SelectState]
@classmethod
- def _create_raw_select(cls, **kw: Any) -> Select:
+ def _create_raw_select(cls, **kw: Any) -> Select[Any]:
"""Create a :class:`.Select` using raw ``__new__`` with no coercions.
Used internally to build up :class:`.Select` constructs with
stmt.__dict__.update(kw)
return stmt
- def __init__(self, *entities: _ColumnsClauseArgument):
+ def __init__(self, *entities: _ColumnsClauseArgument[Any]):
r"""Construct a new :class:`_expression.Select`.
The public constructor for :class:`_expression.Select` is the
cols = list(elem._select_iterable)
return cols[0].type
- def filter(self: SelfSelect, *criteria: ColumnElement[Any]) -> SelfSelect:
+ def filter(
+ self: SelfSelect, *criteria: _ColumnExpressionArgument[bool]
+ ) -> SelfSelect:
"""A synonym for the :meth:`_future.Select.where` method."""
return self.where(*criteria)
return self._raw_columns[0]
- def filter_by(self, **kwargs):
+ if TYPE_CHECKING:
+
+ @overload
+ def scalar_subquery(
+ self: Select[Tuple[_MAYBE_ENTITY]],
+ ) -> ScalarSelect[Any]:
+ ...
+
+ @overload
+ def scalar_subquery(
+ self: Select[Tuple[_NOT_ENTITY]],
+ ) -> ScalarSelect[_NOT_ENTITY]:
+ ...
+
+ @overload
+ def scalar_subquery(self) -> ScalarSelect[Any]:
+ ...
+
+ def scalar_subquery(self) -> ScalarSelect[Any]:
+ ...
+
+ def filter_by(self: SelfSelect, **kwargs: Any) -> SelfSelect:
r"""apply the given filtering criterion as a WHERE clause
to this select.
return self.filter(*clauses)
@property
- def column_descriptions(self):
+ def column_descriptions(self) -> Any:
"""Return a :term:`plugin-enabled` 'column descriptions' structure
referring to the columns which are SELECTed by this statement.
meth = SelectState.get_plugin_class(self).get_column_descriptions
return meth(self)
- def from_statement(self, statement):
+ def from_statement(
+ self, statement: ExecutableReturnsRows
+ ) -> ExecutableReturnsRows:
"""Apply the columns which this :class:`.Select` would select
onto another statement.
)
@property
- def inner_columns(self):
+ def inner_columns(self) -> _SelectIterable:
"""An iterator of all :class:`_expression.ColumnElement`
expressions which would
be rendered into the columns clause of the resulting SELECT statement.
self._reset_memoizations()
- def get_children(self, **kwargs):
+ def get_children(self, **kw: Any) -> Iterable[ClauseElement]:
return itertools.chain(
super(Select, self).get_children(
- omit_attrs=("_from_obj", "_correlate", "_correlate_except")
+ omit_attrs=("_from_obj", "_correlate", "_correlate_except"),
+ **kw,
),
self._iterate_from_elements(),
)
@_generative
def add_columns(
- self: SelfSelect, *columns: _ColumnsClauseArgument
- ) -> SelfSelect:
+ self, *columns: _ColumnsClauseArgument[Any]
+ ) -> Select[Any]:
"""Return a new :func:`_expression.select` construct with
the given column expressions added to its columns clause.
return self
def _set_entities(
- self, entities: Iterable[_ColumnsClauseArgument]
+ self, entities: Iterable[_ColumnsClauseArgument[Any]]
) -> None:
self._raw_columns = [
coercions.expect(
"be removed in a future release. Please use "
":meth:`_expression.Select.add_columns`",
)
- def column(self: SelfSelect, column: _ColumnsClauseArgument) -> SelfSelect:
+ def column(self, column: _ColumnsClauseArgument[Any]) -> Select[Any]:
"""Return a new :func:`_expression.select` construct with
the given column expression added to its columns clause.
return self.add_columns(column)
@util.preload_module("sqlalchemy.sql.util")
- def reduce_columns(
- self: SelfSelect, only_synonyms: bool = True
- ) -> SelfSelect:
+ def reduce_columns(self, only_synonyms: bool = True) -> Select[Any]:
"""Return a new :func:`_expression.select` construct with redundantly
named, equivalently-valued columns removed from the columns clause.
all columns that are equivalent to another are removed.
"""
- return self.with_only_columns(
+ woc: Select[Any]
+ woc = self.with_only_columns(
*util.preloaded.sql_util.reduce_columns(
self._all_selected_columns,
only_synonyms=only_synonyms,
*(self._where_criteria + self._from_obj),
)
)
+ return woc
+
+ # START OVERLOADED FUNCTIONS self.with_only_columns Select 8
+
+ # code within this block is **programmatically,
+ # statically generated** by tools/generate_sel_v1_overloads.py
+
+ @overload
+ def with_only_columns(self, __ent0: _TCCA[_T0]) -> Select[Tuple[_T0]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1]
+ ) -> Select[Tuple[_T0, _T1]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self, __ent0: _TCCA[_T0], __ent1: _TCCA[_T1], __ent2: _TCCA[_T2]
+ ) -> Select[Tuple[_T0, _T1, _T2]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ ) -> Select[Tuple[_T0, _T1, _T2, _T3]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6]]:
+ ...
+
+ @overload
+ def with_only_columns(
+ self,
+ __ent0: _TCCA[_T0],
+ __ent1: _TCCA[_T1],
+ __ent2: _TCCA[_T2],
+ __ent3: _TCCA[_T3],
+ __ent4: _TCCA[_T4],
+ __ent5: _TCCA[_T5],
+ __ent6: _TCCA[_T6],
+ __ent7: _TCCA[_T7],
+ ) -> Select[Tuple[_T0, _T1, _T2, _T3, _T4, _T5, _T6, _T7]]:
+ ...
+
+ # END OVERLOADED FUNCTIONS self.with_only_columns
+
+ @overload
+ def with_only_columns(
+ self,
+ *columns: _ColumnsClauseArgument[Any],
+ maintain_column_froms: bool = False,
+ **__kw: Any,
+ ) -> Select[Any]:
+ ...
@_generative
def with_only_columns(
- self: SelfSelect,
- *columns: _ColumnsClauseArgument,
+ self,
+ *columns: _ColumnsClauseArgument[Any],
maintain_column_froms: bool = False,
- ) -> SelfSelect:
+ **__kw: Any,
+ ) -> Select[Any]:
r"""Return a new :func:`_expression.select` construct with its columns
clause replaced with the given columns.
""" # noqa: E501
+ if __kw:
+ raise _no_kw()
+
# memoizations should be cleared here as of
# I95c560ffcbfa30b26644999412fb6a385125f663 , asserting this
# is the case for now.
return self
@HasMemoized_ro_memoized_attribute
- def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]:
+ def selected_columns(
+ self,
+ ) -> ColumnCollection[str, ColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
representing the columns that
this SELECT statement or similar construct returns in its result set,
by this :class:`_expression.ScalarSelect`.
"""
- self.element = cast(Select, self.element).where(crit)
+ self.element = cast("Select[Any]", self.element).where(crit)
return self
@overload
"""
- self.element = cast(Select, self.element).correlate(*fromclauses)
+ self.element = cast("Select[Any]", self.element).correlate(
+ *fromclauses
+ )
return self
@_generative
"""
- self.element = cast(Select, self.element).correlate_except(
+ self.element = cast("Select[Any]", self.element).correlate_except(
*fromclauses
)
return self
def __init__(
self,
__argument: Optional[
- Union[_ColumnsClauseArgument, SelectBase, ScalarSelect[bool]]
+ Union[_ColumnsClauseArgument[Any], SelectBase, ScalarSelect[Any]]
] = None,
):
+ s: ScalarSelect[Any]
+
+ # TODO: this seems like we should be using coercions for this
if __argument is None:
s = Select(literal_column("*")).scalar_subquery()
- elif isinstance(__argument, (SelectBase, ScalarSelect)):
+ elif isinstance(__argument, SelectBase):
+ s = __argument.scalar_subquery()
+ s._propagate_attrs = __argument._propagate_attrs
+ elif isinstance(__argument, ScalarSelect):
s = __argument
else:
s = Select(__argument).scalar_subquery()
element = fn(element)
return element.self_group(against=operators.exists)
- def select(self) -> Select:
+ def select(self) -> Select[Any]:
r"""Return a SELECT of this :class:`_expression.Exists`.
e.g.::
SelfTextualSelect = typing.TypeVar("SelfTextualSelect", bound="TextualSelect")
-class TextualSelect(SelectBase):
+class TextualSelect(SelectBase, Executable, Generative):
"""Wrap a :class:`_expression.TextClause` construct within a
:class:`_expression.SelectBase`
interface.
self.positional = positional
@HasMemoized_ro_memoized_attribute
- def selected_columns(self) -> ColumnCollection[str, ColumnElement[Any]]:
+ def selected_columns(
+ self,
+ ) -> ColumnCollection[str, KeyedColumnElement[Any]]:
"""A :class:`_expression.ColumnCollection`
representing the columns that
this SELECT statement or similar construct returns in its result set,
from .elements import ColumnClause
from .elements import ColumnElement
from .elements import Grouping
+from .elements import KeyedColumnElement
from .elements import Label
from .elements import Null
from .elements import UnaryExpression
from ._typing import _EquivalentColumnMap
from ._typing import _TypeEngineArgument
from .elements import TextClause
- from .roles import FromClauseRole
from .selectable import _JoinTargetElement
- from .selectable import _OnClauseElement
from .selectable import _SelectIterable
from .selectable import Selectable
from .visitors import _TraverseCallableType
__slots__ = ("row",)
- def __init__(self, row: "Row", max_chars: int = 300):
+ def __init__(self, row: "Row[Any]", max_chars: int = 300):
self.row = row
self.max_chars = max_chars
col = col._annotations["adapt_column"]
if TYPE_CHECKING:
- assert isinstance(col, ColumnElement)
+ assert isinstance(col, KeyedColumnElement)
if self.adapt_from_selectables and col not in self.equivalents:
for adp in self.adapt_from_selectables:
return None
if TYPE_CHECKING:
- assert isinstance(col, ColumnElement)
+ assert isinstance(col, KeyedColumnElement)
if self.include_fn and not self.include_fn(col):
return None
@util.preload_module("sqlalchemy.sql.traversals")
def get_children(
- self, omit_attrs: Tuple[str, ...] = (), **kw: Any
+ self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
) -> Iterable[HasTraverseInternals]:
r"""Return immediate child :class:`.visitors.HasTraverseInternals`
elements of this :class:`.visitors.HasTraverseInternals`.
if typing.TYPE_CHECKING:
def get_children(
- self, omit_attrs: Tuple[str, ...] = (), **kw: Any
+ self, *, omit_attrs: Tuple[str, ...] = (), **kw: Any
) -> Iterable[ExternallyTraversible]:
...
import inspect
import itertools
import operator
+import os
import re
import sys
import textwrap
from typing import Iterator
from typing import List
from typing import Mapping
+from typing import no_type_check
from typing import NoReturn
from typing import Optional
from typing import overload
)
else:
return False
+
+
+@no_type_check
+def console_scripts(
+ path: str, options: dict, ignore_output: bool = False
+) -> None:
+
+ import subprocess
+ import shlex
+ from pathlib import Path
+
+ is_posix = os.name == "posix"
+
+ entrypoint_name = options["entrypoint"]
+
+ for entry in compat.importlib_metadata_get("console_scripts"):
+ if entry.name == entrypoint_name:
+ impl = entry
+ break
+ else:
+ raise Exception(
+ f"Could not find entrypoint console_scripts.{entrypoint_name}"
+ )
+ cmdline_options_str = options.get("options", "")
+ cmdline_options_list = shlex.split(cmdline_options_str, posix=is_posix) + [
+ path
+ ]
+
+ kw = {}
+ if ignore_output:
+ kw["stdout"] = kw["stderr"] = subprocess.DEVNULL
+
+ subprocess.run(
+ [
+ sys.executable,
+ "-c",
+ "import %s; %s.%s()" % (impl.module, impl.module, impl.attr),
+ ]
+ + cmdline_options_list,
+ cwd=Path(__file__).parent.parent,
+ **kw,
+ )
from typing import TypeVar
from typing import Union
-from typing_extensions import NotRequired as NotRequired # noqa
+from typing_extensions import NotRequired as NotRequired
from . import compat
[tool.zimports]
black-line-length = 79
-keep-unused-type-checking = true
[tool.slotscheck]
exclude-modules = '^sqlalchemy\.testing'
eq_(m1.fetchone(), {"a": 1, "b": 1, "c": 1})
eq_(r1.fetchone(), (2, 1, 2))
+ def test_tuples_plus_base(self):
+ r1 = self._fixture()
+
+ t1 = r1.tuples()
+ eq_(t1.fetchone(), (1, 1, 1))
+ eq_(r1.fetchone(), (2, 1, 2))
+
def test_scalar_plus_base(self):
r1 = self._fixture()
u1 = User()
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.*.associationproxy.AssociationProxyInstance\[builtins.set\*?\[builtins.str\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.associationproxy.AssociationProxyInstance\[builtins.set\*?\[builtins.str\]\]
reveal_type(User.email_addresses)
- # EXPECTED_TYPE: builtins.set\*?\[builtins.str\]
+ # EXPECTED_RE_TYPE: builtins.set\*?\[builtins.str\]
reveal_type(u1.email_addresses)
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.engine.base.Engine
+ # EXPECTED_RE_TYPE: sqlalchemy.engine.base.Engine
reveal_type(e)
- # EXPECTED_TYPE: sqlalchemy.engine.reflection.Inspector.*
+ # EXPECTED_RE_TYPE: sqlalchemy.engine.reflection.Inspector.*
reveal_type(insp)
- # EXPECTED_TYPE: .*list.*TypedDict.*ReflectedColumn.*
+ # EXPECTED_RE_TYPE: .*list.*TypedDict.*ReflectedColumn.*
reveal_type(cols)
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Union\[builtins.str, None\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Union\[builtins.str, None\]\]
reveal_type(User.extra)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Union\[builtins.str, None\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Union\[builtins.str, None\]\]
reveal_type(User.extra_name)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.str\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.str\*?\]
reveal_type(Address.email)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.str\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.str\*?\]
reveal_type(Address.email_name)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[experimental_relationship.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[experimental_relationship.Address\]\]
reveal_type(User.addresses_style_one)
- # EXPECTED_TYPE: sqlalchemy.orm.attributes.InstrumentedAttribute\[builtins.set\*?\[experimental_relationship.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.orm.attributes.InstrumentedAttribute\[builtins.set\*?\[experimental_relationship.Address\]\]
reveal_type(User.addresses_style_two)
expr3 = Interval.intersects(i2)
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: builtins.int\*?
+ # EXPECTED_RE_TYPE: builtins.int\*?
reveal_type(i1.length)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
reveal_type(Interval.length)
- # EXPECTED_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr1)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
reveal_type(expr2)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
reveal_type(expr3)
# while we are here, check some Float[] / div type stuff
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.*Function\[builtins.float\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*Function\[builtins.float\*?\]
reveal_type(f1)
- # EXPECTED_TYPE: sqlalchemy.*ColumnElement\[builtins.float\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*ColumnElement\[builtins.float\*?\]
reveal_type(expr)
return expr
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: builtins.int\*?
+ # EXPECTED_RE_TYPE: builtins.int\*?
reveal_type(i1.length)
- # EXPECTED_TYPE: builtins.float\*?
+ # EXPECTED_RE_TYPE: builtins.float\*?
reveal_type(i2.radius)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.int\*?\]
reveal_type(Interval.length)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.float\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.float\*?\]
reveal_type(Interval.radius)
- # EXPECTED_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr1)
- # EXPECTED_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.float\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.SQLCoreOperations\[builtins.float\*?\]
reveal_type(expr2)
- # EXPECTED_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.BinaryExpression\[builtins.bool\*?\]
reveal_type(expr3)
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
-from sqlalchemy import select
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
sess.add_all([Address(user=u1, email="e1"), Address(user=u1, email="e2")])
sess.commit()
-with Session(e) as sess:
- users: List[User] = sess.scalars(
- select(User), execution_options={"stream_results": False}
- ).all()
+ q = sess.query(User).filter_by(id=7)
+
+ # EXPECTED_TYPE: Query[User]
+ reveal_type(q)
+
+ rows1 = q.all()
+
+ # EXPECTED_RE_TYPE: builtins.[Ll]ist\[.*User\*?\]
+ reveal_type(rows1)
+
+ q2 = sess.query(User.id).filter_by(id=7)
+ rows2 = q2.all()
+
+ # EXPECTED_TYPE: List[Row[Tuple[int]]]
+ reveal_type(rows2)
+
+# more result tests in typed_results.py
# as far as if this is ColumnElement, BinaryElement, SQLCoreOperations,
# that might change. main thing is it's SomeSQLColThing[bool] and
# not 'bool' or 'Any'.
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[builtins.bool\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[builtins.bool\]
reveal_type(expr1)
- # EXPECTED_TYPE: sqlalchemy..*ColumnClause\[builtins.str.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnClause\[builtins.str.?\]
reveal_type(c1)
- # EXPECTED_TYPE: sqlalchemy..*ColumnClause\[builtins.int.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnClause\[builtins.int.?\]
reveal_type(c2)
- # EXPECTED_TYPE: sqlalchemy..*BinaryExpression\[builtins.bool\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*BinaryExpression\[builtins.bool\]
reveal_type(expr2)
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[Union\[builtins.float, decimal.Decimal\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[Union\[builtins.float, decimal.Decimal\]\]
reveal_type(expr3)
- # EXPECTED_TYPE: sqlalchemy..*UnaryExpression\[builtins.int.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*UnaryExpression\[builtins.int.?\]
reveal_type(expr4)
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[builtins.bool.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[builtins.bool.?\]
reveal_type(expr5)
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[builtins.bool.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[builtins.bool.?\]
reveal_type(expr6)
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[builtins.str\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[builtins.str\]
reveal_type(expr7)
- # EXPECTED_TYPE: sqlalchemy..*ColumnElement\[builtins.int.?\]
+ # EXPECTED_RE_TYPE: sqlalchemy..*ColumnElement\[builtins.int.?\]
reveal_type(expr8)
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[trad_relationship_uselist.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[trad_relationship_uselist.Address\]\]
reveal_type(User.addresses_style_one)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.set\*?\[trad_relationship_uselist.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.set\*?\[trad_relationship_uselist.Address\]\]
reveal_type(User.addresses_style_two)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(User.addresses_style_three)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(User.addresses_style_three_cast)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(User.addresses_style_four)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_one)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[trad_relationship_uselist.User\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[trad_relationship_uselist.User\*?\]
reveal_type(Address.user_style_one_typed)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_two)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[trad_relationship_uselist.User\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[trad_relationship_uselist.User\*?\]
reveal_type(Address.user_style_two_typed)
# reveal_type(Address.user_style_six)
# reveal_type(Address.user_style_seven)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_eight)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_nine)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_ten)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.dict\*?\[builtins.str, trad_relationship_uselist.User\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.dict\*?\[builtins.str, trad_relationship_uselist.User\]\]
reveal_type(Address.user_style_ten_typed)
if typing.TYPE_CHECKING:
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.Address\]\]
reveal_type(User.addresses_style_one)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.set\*?\[traditional_relationship.Address\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.set\*?\[traditional_relationship.Address\]\]
reveal_type(User.addresses_style_two)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_one)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[traditional_relationship.User\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[traditional_relationship.User\*?\]
reveal_type(Address.user_style_one_typed)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_two)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[traditional_relationship.User\*?\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[traditional_relationship.User\*?\]
reveal_type(Address.user_style_two_typed)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.User\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.User\]\]
reveal_type(Address.user_style_three)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.User\]\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[builtins.list\*?\[traditional_relationship.User\]\]
reveal_type(Address.user_style_four)
- # EXPECTED_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
+ # EXPECTED_RE_TYPE: sqlalchemy.*.InstrumentedAttribute\[Any\]
reveal_type(Address.user_style_five)
--- /dev/null
+from __future__ import annotations
+
+from typing import Tuple
+
+from sqlalchemy import column
+from sqlalchemy import create_engine
+from sqlalchemy import delete
+from sqlalchemy import func
+from sqlalchemy import insert
+from sqlalchemy import Select
+from sqlalchemy import select
+from sqlalchemy import update
+from sqlalchemy.orm import aliased
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
+from sqlalchemy.orm import Session
+
+
+class Base(DeclarativeBase):
+ pass
+
+
+class User(Base):
+ __tablename__ = "user"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str]
+ data: Mapped[str]
+
+
+session = Session()
+
+e = create_engine("sqlite://")
+connection = e.connect()
+
+
+def t_select_1() -> None:
+ stmt = select(User.id, User.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Select[Tuple[int, str]]
+ reveal_type(stmt)
+
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Tuple[int, str]]
+ reveal_type(result)
+
+
+def t_select_2() -> None:
+ stmt = select(User).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Select[Tuple[User]]
+ reveal_type(stmt)
+
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Tuple[User]]
+ reveal_type(result)
+
+
+def t_select_3() -> None:
+ ua = aliased(User)
+
+ # this will fail at runtime, but as we at the moment see aliased(_T)
+ # as _T, typing tools see the constructor as fine.
+ # this line would ideally have a typing error but we'd need the ability
+ # for aliased() to return some namespace of User that's not User.
+ # AsAliased superclass type was tested for this but it had its own
+ # awkwardnesses that aren't really worth it
+ x = ua(id=1, name="foo")
+
+ # EXPECTED_TYPE: Type[User]
+ reveal_type(ua)
+
+ stmt = select(ua.id, ua.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Select[Tuple[int, str]]
+ reveal_type(stmt)
+
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Tuple[int, str]]
+ reveal_type(result)
+
+
+def t_select_4() -> None:
+ ua = aliased(User)
+ stmt = select(ua, User).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Select[Tuple[User, User]]
+ reveal_type(stmt)
+
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Tuple[User, User]]
+ reveal_type(result)
+
+
+def t_legacy_query_single_entity() -> None:
+ q1 = session.query(User).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Query[User]
+ reveal_type(q1)
+
+ # EXPECTED_TYPE: User
+ reveal_type(q1.one())
+
+ # EXPECTED_TYPE: List[User]
+ reveal_type(q1.all())
+
+ # mypy switches to builtins.list for some reason here
+ # EXPECTED_RE_TYPE: .*\.[Ll]ist\[.*Row\*?\[Tuple\[.*User\]\]\]
+ reveal_type(q1.only_return_tuples(True).all())
+
+ # EXPECTED_TYPE: List[Tuple[User]]
+ reveal_type(q1.tuples().all())
+
+
+def t_legacy_query_cols_1() -> None:
+ q1 = session.query(User.id, User.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: RowReturningQuery[Tuple[int, str]]
+ reveal_type(q1)
+
+ # EXPECTED_TYPE: Row[Tuple[int, str]]
+ reveal_type(q1.one())
+
+ r1 = q1.one()
+
+ x, y = r1.t
+
+ # EXPECTED_TYPE: int
+ reveal_type(x)
+
+ # EXPECTED_TYPE: str
+ reveal_type(y)
+
+
+def t_legacy_query_cols_tupleq_1() -> None:
+ q1 = session.query(User.id, User.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: RowReturningQuery[Tuple[int, str]]
+ reveal_type(q1)
+
+ q2 = q1.tuples()
+
+ # EXPECTED_TYPE: Tuple[int, str]
+ reveal_type(q2.one())
+
+ r1 = q2.one()
+
+ x, y = r1
+
+ # EXPECTED_TYPE: int
+ reveal_type(x)
+
+ # EXPECTED_TYPE: str
+ reveal_type(y)
+
+
+def t_legacy_query_cols_1_with_entities() -> None:
+ q1 = session.query(User).filter(User.id == 5)
+
+ # EXPECTED_TYPE: Query[User]
+ reveal_type(q1)
+
+ q2 = q1.with_entities(User.id, User.name)
+
+ # EXPECTED_TYPE: RowReturningQuery[Tuple[int, str]]
+ reveal_type(q2)
+
+ # EXPECTED_TYPE: Row[Tuple[int, str]]
+ reveal_type(q2.one())
+
+ r1 = q2.one()
+
+ x, y = r1.t
+
+ # EXPECTED_TYPE: int
+ reveal_type(x)
+
+ # EXPECTED_TYPE: str
+ reveal_type(y)
+
+
+def t_select_with_only_cols() -> None:
+ q1 = select(User).where(User.id == 5)
+
+ # EXPECTED_TYPE: Select[Tuple[User]]
+ reveal_type(q1)
+
+ q2 = q1.with_only_columns(User.id, User.name)
+
+ # EXPECTED_TYPE: Select[Tuple[int, str]]
+ reveal_type(q2)
+
+ row = connection.execute(q2).one()
+
+ # EXPECTED_TYPE: Row[Tuple[int, str]]
+ reveal_type(row)
+
+ x, y = row.t
+
+ # EXPECTED_TYPE: int
+ reveal_type(x)
+
+ # EXPECTED_TYPE: str
+ reveal_type(y)
+
+
+def t_legacy_query_cols_2() -> None:
+ a1 = aliased(User)
+ q1 = session.query(User, a1, User.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: RowReturningQuery[Tuple[User, User, str]]
+ reveal_type(q1)
+
+ # EXPECTED_TYPE: Row[Tuple[User, User, str]]
+ reveal_type(q1.one())
+
+ r1 = q1.one()
+
+ x, y, z = r1.t
+
+ # EXPECTED_TYPE: User
+ reveal_type(x)
+
+ # EXPECTED_TYPE: User
+ reveal_type(y)
+
+ # EXPECTED_TYPE: str
+ reveal_type(z)
+
+
+def t_legacy_query_cols_2_with_entities() -> None:
+
+ q1 = session.query(User)
+
+ # EXPECTED_TYPE: Query[User]
+ reveal_type(q1)
+
+ a1 = aliased(User)
+ q2 = q1.with_entities(User, a1, User.name).filter(User.id == 5)
+
+ # EXPECTED_TYPE: RowReturningQuery[Tuple[User, User, str]]
+ reveal_type(q2)
+
+ # EXPECTED_TYPE: Row[Tuple[User, User, str]]
+ reveal_type(q2.one())
+
+ r1 = q2.one()
+
+ x, y, z = r1.t
+
+ # EXPECTED_TYPE: User
+ reveal_type(x)
+
+ # EXPECTED_TYPE: User
+ reveal_type(y)
+
+ # EXPECTED_TYPE: str
+ reveal_type(z)
+
+
+def t_select_add_col_loses_type() -> None:
+ q1 = select(User.id, User.name).filter(User.id == 5)
+
+ q2 = q1.add_columns(User.data)
+
+ # note this should not match Select
+ # EXPECTED_TYPE: Select[Any]
+ reveal_type(q2)
+
+
+def t_legacy_query_add_col_loses_type() -> None:
+ q1 = session.query(User.id, User.name).filter(User.id == 5)
+
+ q2 = q1.add_columns(User.data)
+
+ # this should match only Any
+ # EXPECTED_TYPE: Query[Any]
+ reveal_type(q2)
+
+ ua = aliased(User)
+ q3 = q1.add_entity(ua)
+
+ # EXPECTED_TYPE: Query[Any]
+ reveal_type(q3)
+
+
+def t_legacy_query_scalar_subquery() -> None:
+ """scalar subquery should receive the type if first element is a
+ column only"""
+ q1 = session.query(User.id)
+
+ q2 = q1.scalar_subquery()
+
+ # this should be int but mypy can't see it due to the
+ # overload that tries to match an entity.
+ # EXPECTED_RE_TYPE: .*ScalarSelect\[(?:int|Any)\]
+ reveal_type(q2)
+
+ q3 = session.query(User)
+
+ q4 = q3.scalar_subquery()
+
+ # EXPECTED_TYPE: ScalarSelect[Any]
+ reveal_type(q4)
+
+ q5 = session.query(User, User.name)
+
+ q6 = q5.scalar_subquery()
+
+ # EXPECTED_TYPE: ScalarSelect[Any]
+ reveal_type(q6)
+
+ # try to simulate the problem with select()
+ q7 = session.query(User).only_return_tuples(True)
+ q8 = q7.scalar_subquery()
+
+ # EXPECTED_TYPE: ScalarSelect[Any]
+ reveal_type(q8)
+
+
+def t_select_scalar_subquery() -> None:
+ """scalar subquery should receive the type if first element is a
+ column only"""
+ s1 = select(User.id)
+ s2 = s1.scalar_subquery()
+
+ # this should be int but mypy can't see it due to the
+ # overload that tries to match an entity.
+ # EXPECTED_TYPE: ScalarSelect[Any]
+ reveal_type(s2)
+
+ s3 = select(User)
+ s4 = s3.scalar_subquery()
+
+ # it's more important that mypy doesn't get a false positive of
+ # 'User' here
+ # EXPECTED_TYPE: ScalarSelect[Any]
+ reveal_type(s4)
+
+
+def t_select_w_core_selectables() -> None:
+ """things that come from .c. or are FromClause objects currently are not
+ typed. Make sure we are still getting Select at least.
+
+ """
+ s1 = select(User.id, User.name).subquery()
+
+ # EXPECTED_TYPE: KeyedColumnElement[Any]
+ reveal_type(s1.c.name)
+
+ s2 = select(User.id, s1.c.name)
+
+ # this one unfortunately is not working in mypy.
+ # pylance gets the correct type
+ # EXPECTED_TYPE: Select[Tuple[int, Any]]
+ # when experimenting with having a separate TypedSelect class for typing,
+ # mypy would downgrade to Any rather than picking the basemost type.
+ # with typing integrated into Select etc. we can at least get a Select
+ # object back.
+ # EXPECTED_TYPE: Select[Any]
+ reveal_type(s2)
+
+ # so a fully explicit type may be given
+ s2_typed: Select[Tuple[int, str]] = select(User.id, s1.c.name)
+
+ # EXPECTED_TYPE: Select[Tuple[int, str]]
+ reveal_type(s2_typed)
+
+ # plain FromClause etc we at least get Select
+ s3 = select(s1)
+
+ # EXPECTED_TYPE: Select[Any]
+ reveal_type(s3)
+
+ t1 = User.__table__
+ assert t1 is not None
+
+ # EXPECTED_TYPE: FromClause
+ reveal_type(t1)
+
+ s4 = select(t1)
+
+ # EXPECTED_TYPE: Select[Any]
+ reveal_type(s4)
+
+
+def t_dml_insert() -> None:
+ s1 = insert(User).returning(User.id, User.name)
+
+ r1 = session.execute(s1)
+
+ # EXPECTED_TYPE: Result[Tuple[int, str]]
+ reveal_type(r1)
+
+ s2 = insert(User).returning(User)
+
+ r2 = session.execute(s2)
+
+ # EXPECTED_TYPE: Result[Tuple[User]]
+ reveal_type(r2)
+
+ s3 = insert(User).returning(func.foo(), column("q"))
+
+ # EXPECTED_TYPE: ReturningInsert[Any]
+ reveal_type(s3)
+
+ r3 = session.execute(s3)
+
+ # EXPECTED_TYPE: Result[Any]
+ reveal_type(r3)
+
+
+def t_dml_update() -> None:
+ s1 = update(User).returning(User.id, User.name)
+
+ r1 = session.execute(s1)
+
+ # EXPECTED_TYPE: Result[Tuple[int, str]]
+ reveal_type(r1)
+
+
+def t_dml_delete() -> None:
+ s1 = delete(User).returning(User.id, User.name)
+
+ r1 = session.execute(s1)
+
+ # EXPECTED_TYPE: Result[Tuple[int, str]]
+ reveal_type(r1)
--- /dev/null
+from __future__ import annotations
+
+import asyncio
+from typing import cast
+
+from sqlalchemy import Column
+from sqlalchemy import column
+from sqlalchemy import create_engine
+from sqlalchemy import Integer
+from sqlalchemy import select
+from sqlalchemy import table
+from sqlalchemy.ext.asyncio import AsyncConnection
+from sqlalchemy.ext.asyncio import AsyncSession
+from sqlalchemy.ext.asyncio import create_async_engine
+from sqlalchemy.orm import aliased
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
+from sqlalchemy.orm import Session
+
+
+class Base(DeclarativeBase):
+ pass
+
+
+class User(Base):
+ __tablename__ = "user"
+
+ id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str]
+
+
+e = create_engine("sqlite://")
+ae = create_async_engine("sqlite+aiosqlite://")
+
+
+connection = e.connect()
+session = Session(connection)
+
+
+async def async_connect() -> AsyncConnection:
+ return await ae.connect()
+
+
+# the thing with the \*? seems like it could go away
+# as of mypy 0.950
+
+async_connection = asyncio.run(async_connect())
+
+# EXPECTED_RE_TYPE: sqlalchemy..*AsyncConnection\*?
+reveal_type(async_connection)
+
+async_session = AsyncSession(async_connection)
+
+
+# EXPECTED_RE_TYPE: sqlalchemy..*AsyncSession\*?
+reveal_type(async_session)
+
+
+single_stmt = select(User.name).where(User.name == "foo")
+
+# EXPECTED_RE_TYPE: sqlalchemy..*Select\*?\[Tuple\[builtins.str\*?\]\]
+reveal_type(single_stmt)
+
+multi_stmt = select(User.id, User.name).where(User.name == "foo")
+
+# EXPECTED_RE_TYPE: sqlalchemy..*Select\*?\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+reveal_type(multi_stmt)
+
+
+def t_entity_varieties() -> None:
+
+ a1 = aliased(User)
+
+ s1 = select(User.id, User, User.name).where(User.name == "foo")
+
+ r1 = session.execute(s1)
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*.Result\[Tuple\[builtins.int\*?, typed_results.User\*?, builtins.str\*?\]\]
+ reveal_type(r1)
+
+ s2 = select(User, a1).where(User.name == "foo")
+
+ r2 = session.execute(s2)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*Result\[Tuple\[typed_results.User\*?, typed_results.User\*?\]\]
+ reveal_type(r2)
+
+ row = r2.t.one()
+
+ # EXPECTED_RE_TYPE: .*typed_results.User\*?
+ reveal_type(row[0])
+ # EXPECTED_RE_TYPE: .*typed_results.User\*?
+ reveal_type(row[1])
+
+ # testing that plain Mapped[x] gets picked up as well as
+ # aliased class
+ # there is unfortunately no way for attributes on an AliasedClass to be
+ # automatically typed since they are dynamically generated
+ a1_id = cast(Mapped[int], a1.id)
+ s3 = select(User.id, a1_id, a1, User).where(User.name == "foo")
+ # EXPECTED_RE_TYPE: sqlalchemy.*Select\*?\[Tuple\[builtins.int\*?, builtins.int\*?, typed_results.User\*?, typed_results.User\*?\]\]
+ reveal_type(s3)
+
+ # testing Mapped[entity]
+ some_mp = cast(Mapped[User], object())
+ s4 = select(some_mp, a1, User).where(User.name == "foo")
+
+ # NOTEXPECTED_RE_TYPE: sqlalchemy..*Select\*?\[Tuple\[typed_results.User\*?, typed_results.User\*?, typed_results.User\*?\]\]
+
+ # sqlalchemy.sql._gen_overloads.Select[Tuple[typed_results.User, typed_results.User, typed_results.User]]
+
+ # EXPECTED_TYPE: Select[Tuple[User, User, User]]
+ reveal_type(s4)
+
+ # test plain core expressions
+ x = Column("x", Integer)
+ y = x + 5
+
+ s5 = select(x, y, User.name + "hi")
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*Select\*?\[Tuple\[builtins.int\*?, builtins.int\*?\, builtins.str\*?]\]
+ reveal_type(s5)
+
+
+def t_ambiguous_result_type_one() -> None:
+ stmt = select(column("q", Integer), table("x", column("y")))
+
+ # EXPECTED_TYPE: Select[Any]
+ reveal_type(stmt)
+
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Any]
+ reveal_type(result)
+
+
+def t_ambiguous_result_type_two() -> None:
+
+ stmt = select(column("q"))
+
+ # EXPECTED_TYPE: Select[Tuple[Any]]
+ reveal_type(stmt)
+ result = session.execute(stmt)
+
+ # EXPECTED_TYPE: Result[Any]
+ reveal_type(result)
+
+
+def t_aliased() -> None:
+
+ a1 = aliased(User)
+
+ s1 = select(a1)
+ # EXPECTED_TYPE: Select[Tuple[User]]
+ reveal_type(s1)
+
+ s4 = select(a1.name, a1, a1, User).where(User.name == "foo")
+ # EXPECTED_TYPE: Select[Tuple[str, User, User, User]]
+ reveal_type(s4)
+
+
+def t_result_scalar_accessors() -> None:
+ result = connection.execute(single_stmt)
+
+ r1 = result.scalar()
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(r1)
+
+ r2 = result.scalar_one()
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(r2)
+
+ r3 = result.scalar_one_or_none()
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(r3)
+
+ r4 = result.scalars()
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*ScalarResult\[builtins.str.*?\]
+ reveal_type(r4)
+
+ r5 = result.scalars(0)
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*ScalarResult\[builtins.str.*?\]
+ reveal_type(r5)
+
+
+async def t_async_result_scalar_accessors() -> None:
+ result = await async_connection.stream(single_stmt)
+
+ r1 = await result.scalar()
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(r1)
+
+ r2 = await result.scalar_one()
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(r2)
+
+ r3 = await result.scalar_one_or_none()
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(r3)
+
+ r4 = result.scalars()
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*ScalarResult\[builtins.str.*?\]
+ reveal_type(r4)
+
+ r5 = result.scalars(0)
+
+ # EXPECTED_RE_TYPE: sqlalchemy..*ScalarResult\[builtins.str.*?\]
+ reveal_type(r5)
+
+
+def t_connection_execute_multi_row_t() -> None:
+ result = connection.execute(multi_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*CursorResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*Row\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(row)
+
+ x, y = row.t
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+def t_connection_execute_multi() -> None:
+ result = connection.execute(multi_stmt).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+def t_connection_execute_single() -> None:
+ result = connection.execute(single_stmt).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+def t_connection_execute_single_row_scalar() -> None:
+ result = connection.execute(single_stmt).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+
+ x = result.scalar()
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(x)
+
+
+def t_connection_scalar() -> None:
+ obj = connection.scalar(single_stmt)
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(obj)
+
+
+def t_connection_scalars() -> None:
+ result = connection.scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*ScalarResult\[builtins.str\*?\]
+ reveal_type(result)
+ data = result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\[builtins.str\*?\]
+ reveal_type(data)
+
+
+def t_session_execute_multi() -> None:
+ result = session.execute(multi_stmt).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+def t_session_execute_single() -> None:
+ result = session.execute(single_stmt).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+def t_session_scalar() -> None:
+ obj = session.scalar(single_stmt)
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(obj)
+
+
+def t_session_scalars() -> None:
+ result = session.scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*ScalarResult\[builtins.str\*?\]
+ reveal_type(result)
+ data = result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\[builtins.str\*?\]
+ reveal_type(data)
+
+
+async def t_async_connection_execute_multi() -> None:
+ result = (await async_connection.execute(multi_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+async def t_async_connection_execute_single() -> None:
+ result = (await async_connection.execute(single_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+async def t_async_connection_scalar() -> None:
+ obj = await async_connection.scalar(single_stmt)
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(obj)
+
+
+async def t_async_connection_scalars() -> None:
+ result = await async_connection.scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*ScalarResult\*?\[builtins.str\*?\]
+ reveal_type(result)
+ data = result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\[builtins.str\*?\]
+ reveal_type(data)
+
+
+async def t_async_session_execute_multi() -> None:
+ result = (await async_session.execute(multi_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+async def t_async_session_execute_single() -> None:
+ result = (await async_session.execute(single_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+ row = result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+async def t_async_session_scalar() -> None:
+ obj = await async_session.scalar(single_stmt)
+
+ # EXPECTED_RE_TYPE: Union\[builtins.str\*?, None\]
+ reveal_type(obj)
+
+
+async def t_async_session_scalars() -> None:
+ result = await async_session.scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*ScalarResult\*?\[builtins.str\*?\]
+ reveal_type(result)
+ data = result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\[builtins.str\*?\]
+ reveal_type(data)
+
+
+async def t_async_connection_stream_multi() -> None:
+ result = (await async_connection.stream(multi_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = await result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+async def t_async_connection_stream_single() -> None:
+ result = (await async_connection.stream(single_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+ row = await result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+async def t_async_connection_stream_scalars() -> None:
+ result = await async_connection.stream_scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*AsyncScalarResult\*?\[builtins.str\*?\]
+ reveal_type(result)
+ data = await result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\*?\[builtins.str\*?\]
+ reveal_type(data)
+
+
+async def t_async_session_stream_multi() -> None:
+ result = (await async_session.stream(multi_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*TupleResult\[Tuple\[builtins.int\*?, builtins.str\*?\]\]
+ reveal_type(result)
+ row = await result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.int\*?, builtins.str\*?\]
+ reveal_type(row)
+
+ x, y = row
+
+ # EXPECTED_RE_TYPE: builtins.int\*?
+ reveal_type(x)
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(y)
+
+
+async def t_async_session_stream_single() -> None:
+ result = (await async_session.stream(single_stmt)).t
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*AsyncTupleResult\[Tuple\[builtins.str\*?\]\]
+ reveal_type(result)
+ row = await result.one()
+
+ # EXPECTED_RE_TYPE: Tuple\[builtins.str\*?\]
+ reveal_type(row)
+
+ (x,) = row
+
+ # EXPECTED_RE_TYPE: builtins.str\*?
+ reveal_type(x)
+
+
+async def t_async_session_stream_scalars() -> None:
+ result = await async_session.stream_scalars(single_stmt)
+
+ # EXPECTED_RE_TYPE: sqlalchemy.*AsyncScalarResult\*?\[builtins.str\*?\]
+ reveal_type(result)
+ data = await result.all()
+
+ # EXPECTED_RE_TYPE: typing.Sequence\*?\[builtins.str\*?\]
+ reveal_type(data)
_mypy_mapped_attrs = [id, user_id, email_address]
-stmt = select(User.name).where(User.id.in_([1, 2, 3]))
-stmt = select(Address).where(Address.email_address.contains(["foo"]))
+stmt1 = select(User.name).where(User.id.in_([1, 2, 3]))
+stmt2 = select(Address).where(Address.email_address.contains(["foo"]))
expected_msg = re.sub(r"# noqa[:]? ?.*", "", m.group(4))
if is_type:
+ if not is_re:
+ # the goal here is that we can cut-and-paste
+ # from vscode -> pylance into the
+ # EXPECTED_TYPE: line, then the test suite will
+ # validate that line against what mypy produces
+ expected_msg = re.sub(
+ r"([\[\]])",
+ lambda m: rf"\{m.group(0)}",
+ expected_msg,
+ )
+
+ # note making sure preceding text matches
+ # with a dot, so that an expect for "Select"
+ # does not match "TypedSelect"
+ expected_msg = re.sub(
+ r"([\w_]+)",
+ lambda m: rf"(?:.*\.)?{m.group(1)}\*?",
+ expected_msg,
+ )
+
+ expected_msg = re.sub(
+ "List", "builtins.list", expected_msg
+ )
+
+ expected_msg = re.sub(
+ r"(int|str|float|bool)",
+ lambda m: rf"builtins.{m.group(0)}\*?",
+ expected_msg,
+ )
+ # expected_msg = re.sub(
+ # r"(Sequence|Tuple|List|Union)",
+ # lambda m: fr"typing.{m.group(0)}\*?",
+ # expected_msg,
+ # )
+
is_mypy = is_re = True
expected_msg = f'Revealed type is "{expected_msg}"'
current_assert_messages.append(
del output[idx]
if output:
- print("messages from mypy that were not consumed:")
+ print(f"{len(output)} messages from mypy were not consumed:")
print("\n".join(msg for _, msg in output))
assert False, "errors and/or notes remain, see stdout"
assert_raises_message(
sa_exc.ArgumentError,
- "Column expression or FROM clause expected, got "
+ "Column expression, FROM clause, or other .* expected, got "
"<sqlalchemy.sql.selectable.Select .*> object resolved from "
"<AliasedClass .* User> object. To create a FROM clause from "
"a <class 'sqlalchemy.sql.selectable.Select'> object",
assert isinstance(row, collections_abc.Sequence)
assert isinstance(row._mapping, collections_abc.Mapping)
+ def test_single_entity_tuples(self):
+ User = self.classes.User
+ query = fixture_session().query(User).tuples()
+ is_false(query.is_single_entity)
+ row = query.first()
+ assert isinstance(row, collections_abc.Sequence)
+ assert isinstance(row._mapping, collections_abc.Mapping)
+
def test_multiple_entity_false(self):
User = self.classes.User
query = (
assert isinstance(row, collections_abc.Sequence)
assert isinstance(row._mapping, collections_abc.Mapping)
+ def test_multiple_entity_true(self):
+ User = self.classes.User
+ query = fixture_session().query(User.id, User).tuples()
+ is_false(query.is_single_entity)
+ row = query.first()
+ assert isinstance(row, collections_abc.Sequence)
+ assert isinstance(row._mapping, collections_abc.Mapping)
+
class RowTupleTest(QueryTest, AssertsCompiledSQL):
run_setup_mappers = None
from sqlalchemy import MetaData
from sqlalchemy import PrimaryKeyConstraint
from sqlalchemy import schema
+from sqlalchemy import select
from sqlalchemy import Sequence
from sqlalchemy import String
from sqlalchemy import Table
self._assert_fk(t2, None, "p.t1.x", referred_schema_fn=ref_fn)
+ def test_fk_get_referent_is_always_a_column(self):
+ """test the annotation on ForeignKey.get_referent() in that it does
+ in fact return Column even if given a labeled expr in a subquery"""
+
+ m = MetaData()
+ a = Table("a", m, Column("id", Integer, primary_key=True))
+ b = Table("b", m, Column("aid", Integer, ForeignKey("a.id")))
+
+ stmt = select(a.c.id.label("somelabel")).subquery()
+
+ referent = list(b.c.aid.foreign_keys)[0].get_referent(stmt)
+ is_(referent, stmt.c.somelabel)
+ assert isinstance(referent, Column)
+
def test_copy_info(self):
m = MetaData()
fk = ForeignKey("t2.id")
rows.append(row)
eq_(len(rows), 3)
+ def test_scalars(self, connection):
+ users = self.tables.users
+
+ connection.execute(
+ users.insert(),
+ [
+ {"user_id": 7, "user_name": "jack"},
+ {"user_id": 8, "user_name": "ed"},
+ {"user_id": 9, "user_name": "fred"},
+ ],
+ )
+ r = connection.scalars(users.select().order_by(users.c.user_id))
+ eq_(r.all(), [7, 8, 9])
+
+ def test_result_tuples(self, connection):
+ users = self.tables.users
+
+ connection.execute(
+ users.insert(),
+ [
+ {"user_id": 7, "user_name": "jack"},
+ {"user_id": 8, "user_name": "ed"},
+ {"user_id": 9, "user_name": "fred"},
+ ],
+ )
+ r = connection.execute(
+ users.select().order_by(users.c.user_id)
+ ).tuples()
+ eq_(r.all(), [(7, "jack"), (8, "ed"), (9, "fred")])
+
+ def test_row_tuple(self, connection):
+ users = self.tables.users
+
+ connection.execute(
+ users.insert(),
+ [
+ {"user_id": 7, "user_name": "jack"},
+ {"user_id": 8, "user_name": "ed"},
+ {"user_id": 9, "user_name": "fred"},
+ ],
+ )
+ r = connection.execute(users.select().order_by(users.c.user_id))
+ eq_([row.t for row in r], [(7, "jack"), (8, "ed"), (9, "fred")])
+
def test_row_next(self, connection):
users = self.tables.users
def test_old_bracket_style_fail(self):
with expect_raises_message(
exc.ArgumentError,
- r"Column expression or FROM clause expected, "
+ r"Column expression, FROM clause, or other columns clause .*"
r".*Did you mean to say",
):
select([table1.c.myid])
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
+from sqlalchemy.testing import expect_raises_message
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import mock
from sqlalchemy.testing.schema import Column
dialect="mysql",
)
+ def test_update_from_join_unsupported_cases(self):
+ """
+ found_during_typing
+
+ It's unclear how to cleanly guard against this case without producing
+ false positives, particularly due to the support for UPDATE
+ of a CTE. I'm also not sure of the nature of the failure and why
+ it happens this way.
+
+ """
+ users, addresses = self.tables.users, self.tables.addresses
+
+ j = users.join(addresses)
+
+ with expect_raises_message(
+ exc.CompileError,
+ r"Encountered unsupported case when compiling an INSERT or UPDATE "
+ r"statement. If this is a multi-table "
+ r"UPDATE statement, please provide string-named arguments to the "
+ r"values\(\) method with distinct names; support for multi-table "
+ r"UPDATE statements that "
+ r"target multiple tables for UPDATE is very limited",
+ ):
+ update(j).where(addresses.c.email_address == "e1").values(
+ {users.c.id: 10, addresses.c.email_address: "asdf"}
+ ).compile(dialect=mysql.dialect())
+
+ with expect_raises_message(
+ exc.CompileError,
+ r"Encountered unsupported case when compiling an INSERT or UPDATE "
+ r"statement. If this is a multi-table "
+ r"UPDATE statement, please provide string-named arguments to the "
+ r"values\(\) method with distinct names; support for multi-table "
+ r"UPDATE statements that "
+ r"target multiple tables for UPDATE is very limited",
+ ):
+ update(j).where(addresses.c.email_address == "e1").compile(
+ dialect=mysql.dialect()
+ )
+
def test_update_from_join_mysql_whereclause(self):
users, addresses = self.tables.users, self.tables.addresses
alembic "ops" was enhanced to generate a .pyi stubs file statically for
consumption by typing tools.
+Note that the usual OO approach of having a common interface class with
+concrete subtypes doesn't really solve any problems here; the concrete subtypes
+must still list out all methods, arguments, typing annotations, and docstrings,
+all of which is copied by this script rather than requiring it all be
+typed by hand.
+
.. versionadded:: 2.0
"""
import os
from pathlib import Path
import re
-import shlex
import shutil
-import subprocess
import sys
from tempfile import NamedTemporaryFile
import textwrap
from sqlalchemy import util
from sqlalchemy.util import compat
from sqlalchemy.util import langhelpers
+from sqlalchemy.util.langhelpers import console_scripts
from sqlalchemy.util.langhelpers import format_argspec_plus
from sqlalchemy.util.langhelpers import inject_docstring_text
return decorate
+def _grab_overloads(fn):
+ """grab @overload entries for a function, assuming black-formatted
+ code ;) so that we can do a simple regex
+
+ """
+
+ # functions that use @util.deprecated and whatnot will have a string
+ # generated fn. we can look at __wrapped__ but these functions don't
+ # have any overloads in any case right now so skip
+ if fn.__code__.co_filename == "<string>":
+ return []
+
+ with open(fn.__code__.co_filename) as f:
+ lines = [l for i, l in zip(range(fn.__code__.co_firstlineno), f)]
+
+ lines.reverse()
+
+ output = []
+
+ current_ov = []
+ for line in lines[1:]:
+ current_ov.append(line)
+ outside_block_match = re.match(r"^\w", line)
+ if outside_block_match:
+ current_ov[:] = []
+ break
+
+ fn_match = re.match(rf"^ (?:async )?def (.*)\($", line)
+ if fn_match and fn_match.group(1) != fn.__name__:
+ current_ov[:] = []
+ break
+
+ ov_match = re.match(r"^ @overload$", line)
+ if ov_match:
+ output.append("".join(reversed(current_ov)))
+ current_ov[:] = []
+
+ output.reverse()
+ return output
+
+
def process_class(
buf: TextIO,
target_cls: Type[Any],
def instrument(buf: TextIO, name: str, clslevel: bool = False) -> None:
fn = getattr(target_cls, name)
+
+ overloads = _grab_overloads(fn)
+
+ for overload in overloads:
+ buf.write(overload)
+
spec = compat.inspect_getfullargspec(fn)
iscoroutine = inspect.iscoroutinefunction(fn)
"\n # code within this block is "
"**programmatically, \n"
" # statically generated** by"
- " tools/generate_proxy_methods.py\n\n"
+ f" tools/{os.path.basename(__file__)}\n\n"
)
process_class(buf, *args)
return buf.name
-def console_scripts(
- path: str, options: dict, ignore_output: bool = False
-) -> None:
-
- entrypoint_name = options["entrypoint"]
-
- for entry in compat.importlib_metadata_get("console_scripts"):
- if entry.name == entrypoint_name:
- impl = entry
- break
- else:
- raise Exception(
- f"Could not find entrypoint console_scripts.{entrypoint_name}"
- )
- cmdline_options_str = options.get("options", "")
- cmdline_options_list = shlex.split(cmdline_options_str, posix=is_posix) + [
- path
- ]
-
- kw = {}
- if ignore_output:
- kw["stdout"] = kw["stderr"] = subprocess.DEVNULL
-
- subprocess.run(
- [
- sys.executable,
- "-c",
- "import %s; %s.%s()" % (impl.module, impl.module, impl.attr),
- ]
- + cmdline_options_list,
- cwd=Path(__file__).parent.parent,
- **kw,
- )
-
-
def run_module(modname, stdout):
sys.stderr.write(f"importing module {modname}\n")
--- /dev/null
+r"""Generate tuple mapping overloads.
+
+the problem solved by this script is that of there's no way in current
+pep-484 typing to unpack \*args: _T into Tuple[_T]. pep-646 is the first
+pep to provide this, but it doesn't work for the actual Tuple class
+and also mypy does not have support for pep-646 as of yet. Better pep-646
+support would allow us to use a TypeVarTuple with Unpack, but TypeVarTuple
+does not have support for sequence operations like ``__getitem__`` and
+iteration; there's also no way for TypeVarTuple to be translated back to a
+Tuple which does have those things without a combinatoric hardcoding approach
+to each length of tuple.
+
+So here, the script creates a map from `*args` to a Tuple directly using a
+combinatoric generated code approach.
+
+.. versionadded:: 2.0
+
+"""
+from __future__ import annotations
+
+from argparse import ArgumentParser
+import importlib
+import os
+from pathlib import Path
+import re
+import shutil
+import sys
+from tempfile import NamedTemporaryFile
+import textwrap
+
+from sqlalchemy.util.langhelpers import console_scripts
+
+is_posix = os.name == "posix"
+
+
+sys.path.append(str(Path(__file__).parent.parent))
+
+
+def process_module(modname: str, filename: str) -> str:
+
+ # use tempfile in same path as the module, or at least in the
+ # current working directory, so that black / zimports use
+ # local pyproject.toml
+ with NamedTemporaryFile(
+ mode="w", delete=False, suffix=".py", dir=Path(filename).parent
+ ) as buf, open(filename) as orig_py:
+ indent = ""
+ in_block = False
+ current_fnname = given_fnname = None
+ for line in orig_py:
+ m = re.match(
+ r"^( *)# START OVERLOADED FUNCTIONS ([\.\w_]+) ([\w_]+) (\d+)-(\d+)$", # noqa: E501
+ line,
+ )
+ if m:
+ indent = m.group(1)
+ given_fnname = current_fnname = m.group(2)
+ if current_fnname.startswith("self."):
+ use_self = True
+ current_fnname = current_fnname.split(".")[1]
+ else:
+ use_self = False
+ return_type = m.group(3)
+ start_index = int(m.group(4))
+ end_index = int(m.group(5))
+
+ sys.stderr.write(
+ f"Generating {start_index}-{end_index} overloads "
+ f"attributes for "
+ f"class {'self.' if use_self else ''}{current_fnname} "
+ f"-> {return_type}\n"
+ )
+ in_block = True
+ buf.write(line)
+ buf.write(
+ "\n # code within this block is "
+ "**programmatically, \n"
+ " # statically generated** by"
+ f" tools/{os.path.basename(__file__)}\n\n"
+ )
+
+ for num_args in range(start_index, end_index + 1):
+ combinations = [
+ [
+ f"__ent{arg}: _TCCA[_T{arg}]"
+ for arg in range(num_args)
+ ]
+ ]
+ for combination in combinations:
+ buf.write(
+ textwrap.indent(
+ f"""
+@overload
+def {current_fnname}(
+ {'self, ' if use_self else ''}{", ".join(combination)}
+) -> {return_type}[Tuple[{', '.join(f'_T{i}' for i in range(num_args))}]]:
+ ...
+
+""", # noqa: E501
+ indent,
+ )
+ )
+
+ if in_block and line.startswith(
+ f"{indent}# END OVERLOADED FUNCTIONS {given_fnname}"
+ ):
+ in_block = False
+
+ if not in_block:
+ buf.write(line)
+ return buf.name
+
+
+def run_module(modname, stdout):
+
+ sys.stderr.write(f"importing module {modname}\n")
+ mod = importlib.import_module(modname)
+ filename = destination_path = mod.__file__
+ assert filename is not None
+
+ tempfile = process_module(modname, filename)
+
+ ignore_output = stdout
+
+ console_scripts(
+ str(tempfile),
+ {"entrypoint": "zimports"},
+ ignore_output=ignore_output,
+ )
+
+ console_scripts(
+ str(tempfile),
+ {"entrypoint": "black"},
+ ignore_output=ignore_output,
+ )
+
+ if stdout:
+ with open(tempfile) as tf:
+ print(tf.read())
+ os.unlink(tempfile)
+ else:
+ sys.stderr.write(f"Writing {destination_path}...\n")
+ shutil.move(tempfile, destination_path)
+
+
+def main(args):
+ for modname in entries:
+ if args.module in {"all", modname}:
+ run_module(modname, args.stdout)
+
+
+entries = [
+ "sqlalchemy.sql._selectable_constructors",
+ "sqlalchemy.orm.session",
+ "sqlalchemy.orm.query",
+ "sqlalchemy.sql.selectable",
+ "sqlalchemy.sql.dml",
+]
+
+if __name__ == "__main__":
+ parser = ArgumentParser()
+ parser.add_argument(
+ "--module",
+ choices=entries + ["all"],
+ default="all",
+ help="Which file to generate. Default is to regenerate all files",
+ )
+ parser.add_argument(
+ "--stdout",
+ action="store_true",
+ help="Write to stdout instead of saving to file",
+ )
+ args = parser.parse_args()
+ main(args)