summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/base.py49
-rw-r--r--lib/sqlalchemy/sql/ddl.py12
-rw-r--r--lib/sqlalchemy/sql/dml.py61
-rw-r--r--lib/sqlalchemy/sql/elements.py9
-rw-r--r--lib/sqlalchemy/sql/expression.py8
-rw-r--r--lib/sqlalchemy/sql/selectable.py142
6 files changed, 218 insertions, 63 deletions
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index 187651435..30d589258 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -16,6 +16,7 @@ import itertools
from itertools import zip_longest
import operator
import re
+import typing
from . import roles
from . import visitors
@@ -29,6 +30,7 @@ from .. import exc
from .. import util
from ..util import HasMemoized
from ..util import hybridmethod
+from ..util import typing as compat_typing
try:
from sqlalchemy.cyextension.util import prefix_anon_map # noqa
@@ -42,6 +44,10 @@ type_api = None
NO_ARG = util.symbol("NO_ARG")
+# if I use sqlalchemy.util.typing, which has the exact same
+# symbols, mypy reports: "error: _Fn? not callable"
+_Fn = typing.TypeVar("_Fn", bound=typing.Callable)
+
class Immutable:
"""mark a ClauseElement as 'immutable' when expressions are cloned."""
@@ -101,7 +107,16 @@ def _select_iterables(elements):
)
-def _generative(fn):
+_Self = typing.TypeVar("_Self", bound="_GenerativeType")
+_Args = compat_typing.ParamSpec("_Args")
+
+
+class _GenerativeType(compat_typing.Protocol):
+ def _generate(self: "_Self") -> "_Self":
+ ...
+
+
+def _generative(fn: _Fn) -> _Fn:
"""non-caching _generative() decorator.
This is basically the legacy decorator that copies the object and
@@ -110,14 +125,14 @@ def _generative(fn):
"""
@util.decorator
- def _generative(fn, self, *args, **kw):
+ def _generative(
+ fn: _Fn, self: _Self, *args: _Args.args, **kw: _Args.kwargs
+ ) -> _Self:
"""Mark a method as generative."""
self = self._generate()
x = fn(self, *args, **kw)
- assert (
- x is None or x is self
- ), "generative methods must return None or self"
+ assert x is self, "generative methods must return self"
return self
decorated = _generative(fn)
@@ -788,6 +803,9 @@ class ExecutableOption(HasCopyInternals):
return c
+SelfExecutable = typing.TypeVar("SelfExecutable", bound="Executable")
+
+
class Executable(roles.StatementRole, Generative):
"""Mark a :class:`_expression.ClauseElement` as supporting execution.
@@ -824,7 +842,7 @@ class Executable(roles.StatementRole, Generative):
return self.__visit_name__
@_generative
- def options(self, *options):
+ def options(self: SelfExecutable, *options) -> SelfExecutable:
"""Apply options to this statement.
In the general sense, options are any kind of Python object
@@ -857,9 +875,12 @@ class Executable(roles.StatementRole, Generative):
coercions.expect(roles.ExecutableOptionRole, opt)
for opt in options
)
+ return self
@_generative
- def _set_compile_options(self, compile_options):
+ def _set_compile_options(
+ self: SelfExecutable, compile_options
+ ) -> SelfExecutable:
"""Assign the compile options to a new value.
:param compile_options: appropriate CacheableOptions structure
@@ -867,15 +888,21 @@ class Executable(roles.StatementRole, Generative):
"""
self._compile_options = compile_options
+ return self
@_generative
- def _update_compile_options(self, options):
+ def _update_compile_options(
+ self: SelfExecutable, options
+ ) -> SelfExecutable:
"""update the _compile_options with new keys."""
self._compile_options += options
+ return self
@_generative
- def _add_context_option(self, callable_, cache_args):
+ def _add_context_option(
+ self: SelfExecutable, callable_, cache_args
+ ) -> SelfExecutable:
"""Add a context option to this statement.
These are callable functions that will
@@ -887,9 +914,10 @@ class Executable(roles.StatementRole, Generative):
"""
self._with_context_options += ((callable_, cache_args),)
+ return self
@_generative
- def execution_options(self, **kw):
+ def execution_options(self: SelfExecutable, **kw) -> SelfExecutable:
"""Set non-SQL options for the statement which take effect during
execution.
@@ -1004,6 +1032,7 @@ class Executable(roles.StatementRole, Generative):
"on Connection.execution_options(), not per statement."
)
self._execution_options = self._execution_options.union(kw)
+ return self
def get_execution_options(self):
"""Get the non-SQL options which will take effect during execution.
diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py
index f415aeaff..ad22fa6da 100644
--- a/lib/sqlalchemy/sql/ddl.py
+++ b/lib/sqlalchemy/sql/ddl.py
@@ -9,6 +9,7 @@ Provides the hierarchy of DDL-defining schema items as well as routines
to invoke them for a create/drop call.
"""
+import typing
from . import roles
from .base import _generative
@@ -34,6 +35,9 @@ class _DDLCompiles(ClauseElement):
raise NotImplementedError()
+SelfDDLElement = typing.TypeVar("SelfDDLElement", bound="DDLElement")
+
+
class DDLElement(roles.DDLRole, Executable, _DDLCompiles):
"""Base class for DDL expression constructs.
@@ -77,7 +81,7 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles):
)
@_generative
- def against(self, target):
+ def against(self: SelfDDLElement, target) -> SelfDDLElement:
"""Return a copy of this :class:`_schema.DDLElement` which will include
the given target.
@@ -111,9 +115,12 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles):
"""
self.target = target
+ return self
@_generative
- def execute_if(self, dialect=None, callable_=None, state=None):
+ def execute_if(
+ self: SelfDDLElement, dialect=None, callable_=None, state=None
+ ) -> SelfDDLElement:
r"""Return a callable that will execute this
:class:`_ddl.DDLElement` conditionally within an event handler.
@@ -181,6 +188,7 @@ class DDLElement(roles.DDLRole, Executable, _DDLCompiles):
self.dialect = dialect
self.callable_ = callable_
self.state = state
+ return self
def _should_execute(self, target, bind, **kw):
if isinstance(self.dialect, str):
diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py
index 7b3716a68..ab0a05651 100644
--- a/lib/sqlalchemy/sql/dml.py
+++ b/lib/sqlalchemy/sql/dml.py
@@ -10,8 +10,8 @@ Provide :class:`_expression.Insert`, :class:`_expression.Update` and
"""
import collections.abc as collections_abc
+import typing
-from sqlalchemy.types import NullType
from . import coercions
from . import roles
from . import util as sql_util
@@ -30,6 +30,7 @@ from .elements import Null
from .selectable import HasCTE
from .selectable import HasPrefixes
from .selectable import ReturnsRows
+from .sqltypes import NullType
from .visitors import InternalTraversal
from .. import exc
from .. import util
@@ -210,6 +211,9 @@ class DeleteDMLState(DMLState):
self._extra_froms = self._make_extra_froms(statement)
+SelfUpdateBase = typing.TypeVar("SelfUpdateBase", bound="UpdateBase")
+
+
class UpdateBase(
roles.DMLRole,
HasCTE,
@@ -313,7 +317,7 @@ class UpdateBase(
)
@_generative
- def with_dialect_options(self, **opt):
+ def with_dialect_options(self: SelfUpdateBase, **opt) -> SelfUpdateBase:
"""Add dialect options to this INSERT/UPDATE/DELETE object.
e.g.::
@@ -326,6 +330,7 @@ class UpdateBase(
"""
self._validate_dialect_kwargs(opt)
+ return self
def _validate_dialect_kwargs_deprecated(self, dialect_kw):
util.warn_deprecated_20(
@@ -337,7 +342,7 @@ class UpdateBase(
self._validate_dialect_kwargs(dialect_kw)
@_generative
- def returning(self, *cols):
+ def returning(self: SelfUpdateBase, *cols) -> SelfUpdateBase:
r"""Add a :term:`RETURNING` or equivalent clause to this statement.
e.g.:
@@ -414,6 +419,7 @@ class UpdateBase(
self._returning += tuple(
coercions.expect(roles.ColumnsClauseRole, c) for c in cols
)
+ return self
@property
def _all_selected_columns(self):
@@ -433,7 +439,9 @@ class UpdateBase(
).as_immutable()
@_generative
- def with_hint(self, text, selectable=None, dialect_name="*"):
+ def with_hint(
+ self: SelfUpdateBase, text, selectable=None, dialect_name="*"
+ ) -> SelfUpdateBase:
"""Add a table hint for a single table to this
INSERT/UPDATE/DELETE statement.
@@ -467,6 +475,10 @@ class UpdateBase(
selectable = self.table
self._hints = self._hints.union({(selectable, dialect_name): text})
+ return self
+
+
+SelfValuesBase = typing.TypeVar("SelfValuesBase", bound="ValuesBase")
class ValuesBase(UpdateBase):
@@ -506,7 +518,7 @@ class ValuesBase(UpdateBase):
"values present",
},
)
- def values(self, *args, **kwargs):
+ def values(self: SelfValuesBase, *args, **kwargs) -> SelfValuesBase:
r"""Specify a fixed VALUES clause for an INSERT statement, or the SET
clause for an UPDATE.
@@ -643,7 +655,7 @@ class ValuesBase(UpdateBase):
if arg and isinstance(arg[0], (list, dict, tuple)):
self._multi_values += (arg,)
- return
+ return self
# tuple values
arg = {c.key: value for c, value in zip(self.table.c, arg)}
@@ -681,6 +693,7 @@ class ValuesBase(UpdateBase):
self._values = self._values.union(arg)
else:
self._values = util.immutabledict(arg)
+ return self
@_generative
@_exclusive_against(
@@ -690,7 +703,7 @@ class ValuesBase(UpdateBase):
},
defaults={"_returning": _returning},
)
- def return_defaults(self, *cols):
+ def return_defaults(self: SelfValuesBase, *cols) -> SelfValuesBase:
"""Make use of a :term:`RETURNING` clause for the purpose
of fetching server-side expressions and defaults.
@@ -776,6 +789,10 @@ class ValuesBase(UpdateBase):
"""
self._return_defaults = True
self._return_defaults_columns = cols
+ return self
+
+
+SelfInsert = typing.TypeVar("SelfInsert", bound="Insert")
class Insert(ValuesBase):
@@ -918,7 +935,7 @@ class Insert(ValuesBase):
self._return_defaults_columns = return_defaults
@_generative
- def inline(self):
+ def inline(self: SelfInsert) -> SelfInsert:
"""Make this :class:`_expression.Insert` construct "inline" .
When set, no attempt will be made to retrieve the
@@ -936,9 +953,12 @@ class Insert(ValuesBase):
"""
self._inline = True
+ return self
@_generative
- def from_select(self, names, select, include_defaults=True):
+ def from_select(
+ self: SelfInsert, names, select, include_defaults=True
+ ) -> SelfInsert:
"""Return a new :class:`_expression.Insert` construct which represents
an ``INSERT...FROM SELECT`` statement.
@@ -997,13 +1017,17 @@ class Insert(ValuesBase):
self._inline = True
self.include_insert_from_select_defaults = include_defaults
self.select = coercions.expect(roles.DMLSelectRole, select)
+ return self
+
+
+SelfDMLWhereBase = typing.TypeVar("SelfDMLWhereBase", bound="DMLWhereBase")
class DMLWhereBase:
_where_criteria = ()
@_generative
- def where(self, *whereclause):
+ def where(self: SelfDMLWhereBase, *whereclause) -> SelfDMLWhereBase:
"""Return a new construct with the given expression(s) added to
its WHERE clause, joined to the existing clause via AND, if any.
@@ -1037,8 +1061,9 @@ class DMLWhereBase:
for criterion in whereclause:
where_criteria = coercions.expect(roles.WhereHavingRole, criterion)
self._where_criteria += (where_criteria,)
+ return self
- def filter(self, *criteria):
+ def filter(self: SelfDMLWhereBase, *criteria) -> SelfDMLWhereBase:
"""A synonym for the :meth:`_dml.DMLWhereBase.where` method.
.. versionadded:: 1.4
@@ -1050,7 +1075,7 @@ class DMLWhereBase:
def _filter_by_zero(self):
return self.table
- def filter_by(self, **kwargs):
+ def filter_by(self: SelfDMLWhereBase, **kwargs) -> SelfDMLWhereBase:
r"""apply the given filtering criterion as a WHERE clause
to this select.
@@ -1081,6 +1106,9 @@ class DMLWhereBase:
)
+SelfUpdate = typing.TypeVar("SelfUpdate", bound="Update")
+
+
class Update(DMLWhereBase, ValuesBase):
"""Represent an Update construct.
@@ -1261,7 +1289,7 @@ class Update(DMLWhereBase, ValuesBase):
self._return_defaults = return_defaults
@_generative
- def ordered_values(self, *args):
+ def ordered_values(self: SelfUpdate, *args) -> SelfUpdate:
"""Specify the VALUES clause of this UPDATE statement with an explicit
parameter ordering that will be maintained in the SET clause of the
resulting UPDATE statement.
@@ -1295,9 +1323,10 @@ class Update(DMLWhereBase, ValuesBase):
kv_generator = DMLState.get_plugin_class(self)._get_crud_kv_pairs
self._ordered_values = kv_generator(self, args)
+ return self
@_generative
- def inline(self):
+ def inline(self: SelfUpdate) -> SelfUpdate:
"""Make this :class:`_expression.Update` construct "inline" .
When set, SQL defaults present on :class:`_schema.Column`
@@ -1313,6 +1342,10 @@ class Update(DMLWhereBase, ValuesBase):
"""
self._inline = True
+ return self
+
+
+SelfDelete = typing.TypeVar("SelfDelete", bound="Delete")
class Delete(DMLWhereBase, UpdateBase):
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 37425345b..f6606e01d 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -13,6 +13,7 @@
import itertools
import operator
import re
+import typing
from . import coercions
from . import operators
@@ -1719,6 +1720,9 @@ class TypeClause(ClauseElement):
self.type = type_
+SelfTextClause = typing.TypeVar("SelfTextClause", bound="TextClause")
+
+
class TextClause(
roles.DDLConstraintColumnRole,
roles.DDLExpressionRole,
@@ -1875,7 +1879,9 @@ class TextClause(
return TextClause(text)
@_generative
- def bindparams(self, *binds, **names_to_values):
+ def bindparams(
+ self: SelfTextClause, *binds, **names_to_values
+ ) -> SelfTextClause:
"""Establish the values and/or types of bound parameters within
this :class:`_expression.TextClause` construct.
@@ -2000,6 +2006,7 @@ class TextClause(
) from err
else:
new_params[key] = existing._with_value(value, required=False)
+ return self
@util.preload_module("sqlalchemy.sql.selectable")
def columns(self, *cols, **types):
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 407f1dd33..89b7c6596 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -88,6 +88,8 @@ __all__ = [
]
+from typing import Callable
+
from .base import _from_objects
from .base import _select_iterables
from .base import ColumnCollection
@@ -175,10 +177,8 @@ from .traversals import CacheKey
from .visitors import Visitable
from ..util.langhelpers import public_factory
-# factory functions - these pull class-bound constructors and classmethods
-# from SQL elements and selectables into public functions. This allows
-# the functions to be available in the sqlalchemy.sql.* namespace and
-# to be auto-cross-documenting from the function to the class itself.
+# TODO: proposal is to remove public_factory and replace with traditional
+# functions exported here.
all_ = public_factory(CollectionAggregate._create_all, ".sql.expression.all_")
any_ = public_factory(CollectionAggregate._create_any, ".sql.expression.any_")
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index 802576b89..8b35dc6ac 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -14,6 +14,9 @@ SQL tables and derived rowsets.
import collections
import itertools
from operator import attrgetter
+import typing
+from typing import Type
+from typing import Union
from . import coercions
from . import operators
@@ -209,6 +212,9 @@ class Selectable(ReturnsRows):
)
+SelfHasPrefixes = typing.TypeVar("SelfHasPrefixes", bound="HasPrefixes")
+
+
class HasPrefixes:
_prefixes = ()
@@ -222,7 +228,7 @@ class HasPrefixes:
":meth:`_expression.HasPrefixes.prefix_with`",
":paramref:`.HasPrefixes.prefix_with.*expr`",
)
- def prefix_with(self, *expr, **kw):
+ def prefix_with(self: SelfHasPrefixes, *expr, **kw) -> SelfHasPrefixes:
r"""Add one or more expressions following the statement keyword, i.e.
SELECT, INSERT, UPDATE, or DELETE. Generative.
@@ -255,6 +261,7 @@ class HasPrefixes:
"Unsupported argument(s): %s" % ",".join(kw)
)
self._setup_prefixes(expr, dialect)
+ return self
def _setup_prefixes(self, prefixes, dialect=None):
self._prefixes = self._prefixes + tuple(
@@ -265,6 +272,9 @@ class HasPrefixes:
)
+SelfHasSuffixes = typing.TypeVar("SelfHasSuffixes", bound="HasSuffixes")
+
+
class HasSuffixes:
_suffixes = ()
@@ -278,7 +288,7 @@ class HasSuffixes:
":meth:`_expression.HasSuffixes.suffix_with`",
":paramref:`.HasSuffixes.suffix_with.*expr`",
)
- def suffix_with(self, *expr, **kw):
+ def suffix_with(self: SelfHasSuffixes, *expr, **kw) -> SelfHasSuffixes:
r"""Add one or more expressions following the statement as a whole.
This is used to support backend-specific suffix keywords on
@@ -306,6 +316,7 @@ class HasSuffixes:
"Unsupported argument(s): %s" % ",".join(kw)
)
self._setup_suffixes(expr, dialect)
+ return self
def _setup_suffixes(self, suffixes, dialect=None):
self._suffixes = self._suffixes + tuple(
@@ -316,6 +327,9 @@ class HasSuffixes:
)
+SelfHasHints = typing.TypeVar("SelfHasHints", bound="HasHints")
+
+
class HasHints:
_hints = util.immutabledict()
_statement_hints = ()
@@ -352,7 +366,9 @@ class HasHints:
return self.with_hint(None, text, dialect_name)
@_generative
- def with_hint(self, selectable, text, dialect_name="*"):
+ def with_hint(
+ self: SelfHasHints, selectable, text, dialect_name="*"
+ ) -> SelfHasHints:
r"""Add an indexing or other executional context hint for the given
selectable to this :class:`_expression.Select` or other selectable
object.
@@ -398,6 +414,7 @@ class HasHints:
): text
}
)
+ return self
class FromClause(roles.AnonymizedFromClauseRole, Selectable):
@@ -2082,6 +2099,9 @@ class CTE(
return self._restates if self._restates is not None else self
+SelfHasCTE = typing.TypeVar("SelfHasCTE", bound="HasCTE")
+
+
class HasCTE(roles.HasCTERole):
"""Mixin that declares a class to include CTE support.
@@ -2096,7 +2116,7 @@ class HasCTE(roles.HasCTERole):
_independent_ctes = ()
@_generative
- def add_cte(self, cte):
+ def add_cte(self: SelfHasCTE, cte) -> SelfHasCTE:
"""Add a :class:`_sql.CTE` to this statement object that will be
independently rendered even if not referenced in the statement
otherwise.
@@ -2161,6 +2181,7 @@ class HasCTE(roles.HasCTERole):
"""
cte = coercions.expect(roles.IsCTERole, cte)
self._independent_ctes += (cte,)
+ return self
def cte(self, name=None, recursive=False, nesting=False):
r"""Return a new :class:`_expression.CTE`,
@@ -2759,6 +2780,9 @@ class ForUpdateArg(ClauseElement):
self.of = None
+SelfValues = typing.TypeVar("SelfValues", bound="Values")
+
+
class Values(Generative, FromClause):
"""Represent a ``VALUES`` construct that can be used as a FROM element
in a statement.
@@ -2829,7 +2853,7 @@ class Values(Generative, FromClause):
return [col.type for col in self._column_args]
@_generative
- def alias(self, name, **kw):
+ def alias(self: SelfValues, name, **kw) -> SelfValues:
"""Return a new :class:`_expression.Values`
construct that is a copy of this
one with the given name.
@@ -2846,9 +2870,10 @@ class Values(Generative, FromClause):
"""
self.name = name
self.named_with_column = self.name is not None
+ return self
@_generative
- def lateral(self, name=None):
+ def lateral(self: SelfValues, name=None) -> SelfValues:
"""Return a new :class:`_expression.Values` with the lateral flag set,
so that
it renders as LATERAL.
@@ -2861,9 +2886,10 @@ class Values(Generative, FromClause):
self._is_lateral = True
if name is not None:
self.name = name
+ return self
@_generative
- def data(self, values):
+ def data(self: SelfValues, values) -> SelfValues:
"""Return a new :class:`_expression.Values` construct,
adding the given data
to the data list.
@@ -2879,6 +2905,7 @@ class Values(Generative, FromClause):
"""
self._data += (values,)
+ return self
def _populate_column_collection(self):
for c in self._column_args:
@@ -3312,6 +3339,11 @@ class DeprecatedSelectBaseGenerations:
self.group_by.non_generative(self, *clauses)
+SelfGenerativeSelect = typing.TypeVar(
+ "SelfGenerativeSelect", bound="GenerativeSelect"
+)
+
+
class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
"""Base class for SELECT statements where additional elements can be
added.
@@ -3371,13 +3403,13 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
@_generative
def with_for_update(
- self,
+ self: SelfGenerativeSelect,
nowait=False,
read=False,
of=None,
skip_locked=False,
key_share=False,
- ):
+ ) -> SelfGenerativeSelect:
"""Specify a ``FOR UPDATE`` clause for this
:class:`_expression.GenerativeSelect`.
@@ -3430,6 +3462,7 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
skip_locked=skip_locked,
key_share=key_share,
)
+ return self
def get_label_style(self):
"""
@@ -3573,7 +3606,7 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
)
@_generative
- def limit(self, limit):
+ def limit(self: SelfGenerativeSelect, limit) -> SelfGenerativeSelect:
"""Return a new selectable with the given LIMIT criterion
applied.
@@ -3603,9 +3636,12 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
self._fetch_clause = self._fetch_clause_options = None
self._limit_clause = self._offset_or_limit_clause(limit)
+ return self
@_generative
- def fetch(self, count, with_ties=False, percent=False):
+ def fetch(
+ self: SelfGenerativeSelect, count, with_ties=False, percent=False
+ ) -> SelfGenerativeSelect:
"""Return a new selectable with the given FETCH FIRST criterion
applied.
@@ -3653,9 +3689,10 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
"with_ties": with_ties,
"percent": percent,
}
+ return self
@_generative
- def offset(self, offset):
+ def offset(self: SelfGenerativeSelect, offset) -> SelfGenerativeSelect:
"""Return a new selectable with the given OFFSET criterion
applied.
@@ -3681,10 +3718,11 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
"""
self._offset_clause = self._offset_or_limit_clause(offset)
+ return self
@_generative
@util.preload_module("sqlalchemy.sql.util")
- def slice(self, start, stop):
+ def slice(self: SelfGenerativeSelect, start, stop) -> SelfGenerativeSelect:
"""Apply LIMIT / OFFSET to this statement based on a slice.
The start and stop indices behave like the argument to Python's
@@ -3728,9 +3766,10 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
self._limit_clause, self._offset_clause = sql_util._make_slice(
self._limit_clause, self._offset_clause, start, stop
)
+ return self
@_generative
- def order_by(self, *clauses):
+ def order_by(self: SelfGenerativeSelect, *clauses) -> SelfGenerativeSelect:
r"""Return a new selectable with the given list of ORDER BY
criteria applied.
@@ -3764,9 +3803,10 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
coercions.expect(roles.OrderByRole, clause)
for clause in clauses
)
+ return self
@_generative
- def group_by(self, *clauses):
+ def group_by(self: SelfGenerativeSelect, *clauses) -> SelfGenerativeSelect:
r"""Return a new selectable with the given list of GROUP BY
criterion applied.
@@ -3797,6 +3837,7 @@ class GenerativeSelect(DeprecatedSelectBaseGenerations, SelectBase):
coercions.expect(roles.GroupByRole, clause)
for clause in clauses
)
+ return self
@CompileState.plugin_for("default", "compound_select")
@@ -4658,6 +4699,10 @@ class _MemoizedSelectEntities(
) = select_stmt._with_options = ()
+# TODO: use pep-673 when feasible
+SelfSelect = typing.TypeVar("SelfSelect", bound="Select")
+
+
class Select(
HasPrefixes,
HasSuffixes,
@@ -4737,7 +4782,9 @@ class Select(
]
@classmethod
- def _create(cls, *entities) -> "Select":
+ def _create(
+ cls, *entities: Union[roles.ColumnsClauseRole, Type]
+ ) -> "Select":
r"""Construct a new :class:`_expression.Select`.
@@ -4788,7 +4835,7 @@ class Select(
return self
@classmethod
- def _create_raw_select(cls, **kw):
+ def _create_raw_select(cls, **kw) -> "Select":
"""Create a :class:`.Select` using raw ``__new__`` with no coercions.
Used internally to build up :class:`.Select` constructs with
@@ -4873,7 +4920,9 @@ class Select(
return meth(self, statement)
@_generative
- def join(self, target, onclause=None, isouter=False, full=False):
+ def join(
+ self: SelfSelect, target, onclause=None, isouter=False, full=False
+ ) -> SelfSelect:
r"""Create a SQL JOIN against this :class:`_expression.Select`
object's criterion
and apply generatively, returning the newly resulting
@@ -4939,6 +4988,7 @@ class Select(
self._setup_joins += (
(target, onclause, None, {"isouter": isouter, "full": full}),
)
+ return self
def outerjoin_from(self, from_, target, onclause=None, full=False):
r"""Create a SQL LEFT OUTER JOIN against this :class:`_expression.Select`
@@ -4955,8 +5005,13 @@ class Select(
@_generative
def join_from(
- self, from_, target, onclause=None, isouter=False, full=False
- ):
+ self: SelfSelect,
+ from_,
+ target,
+ onclause=None,
+ isouter=False,
+ full=False,
+ ) -> SelfSelect:
r"""Create a SQL JOIN against this :class:`_expression.Select`
object's criterion
and apply generatively, returning the newly resulting
@@ -5014,6 +5069,7 @@ class Select(
self._setup_joins += (
(target, onclause, from_, {"isouter": isouter, "full": full}),
)
+ return self
def outerjoin(self, target, onclause=None, full=False):
"""Create a left outer join.
@@ -5211,7 +5267,7 @@ class Select(
)
@_generative
- def add_columns(self, *columns):
+ def add_columns(self: SelfSelect, *columns) -> SelfSelect:
"""Return a new :func:`_expression.select` construct with
the given column expressions added to its columns clause.
@@ -5233,6 +5289,7 @@ class Select(
)
for column in columns
]
+ return self
def _set_entities(self, entities):
self._raw_columns = [
@@ -5297,7 +5354,7 @@ class Select(
)
@_generative
- def with_only_columns(self, *columns, **kw):
+ def with_only_columns(self: SelfSelect, *columns, **kw) -> SelfSelect:
r"""Return a new :func:`_expression.select` construct with its columns
clause replaced with the given columns.
@@ -5372,6 +5429,7 @@ class Select(
"columns", "Select.with_only_columns", columns
)
]
+ return self
@property
def whereclause(self):
@@ -5393,7 +5451,7 @@ class Select(
_whereclause = whereclause
@_generative
- def where(self, *whereclause):
+ def where(self: SelfSelect, *whereclause) -> SelfSelect:
"""Return a new :func:`_expression.select` construct with
the given expression added to
its WHERE clause, joined to the existing clause via AND, if any.
@@ -5405,9 +5463,10 @@ class Select(
for criterion in whereclause:
where_criteria = coercions.expect(roles.WhereHavingRole, criterion)
self._where_criteria += (where_criteria,)
+ return self
@_generative
- def having(self, having):
+ def having(self: SelfSelect, having) -> SelfSelect:
"""Return a new :func:`_expression.select` construct with
the given expression added to
its HAVING clause, joined to the existing clause via AND, if any.
@@ -5416,9 +5475,10 @@ class Select(
self._having_criteria += (
coercions.expect(roles.WhereHavingRole, having),
)
+ return self
@_generative
- def distinct(self, *expr):
+ def distinct(self: SelfSelect, *expr) -> SelfSelect:
r"""Return a new :func:`_expression.select` construct which
will apply DISTINCT to its columns clause.
@@ -5437,9 +5497,10 @@ class Select(
)
else:
self._distinct = True
+ return self
@_generative
- def select_from(self, *froms):
+ def select_from(self: SelfSelect, *froms) -> SelfSelect:
r"""Return a new :func:`_expression.select` construct with the
given FROM expression(s)
merged into its list of FROM objects.
@@ -5480,9 +5541,10 @@ class Select(
)
for fromclause in froms
)
+ return self
@_generative
- def correlate(self, *fromclauses):
+ def correlate(self: SelfSelect, *fromclauses) -> SelfSelect:
r"""Return a new :class:`_expression.Select`
which will correlate the given FROM
clauses to that of an enclosing :class:`_expression.Select`.
@@ -5541,9 +5603,10 @@ class Select(
self._correlate = self._correlate + tuple(
coercions.expect(roles.FromClauseRole, f) for f in fromclauses
)
+ return self
@_generative
- def correlate_except(self, *fromclauses):
+ def correlate_except(self: SelfSelect, *fromclauses) -> SelfSelect:
r"""Return a new :class:`_expression.Select`
which will omit the given FROM
clauses from the auto-correlation process.
@@ -5579,6 +5642,7 @@ class Select(
self._correlate_except = (self._correlate_except or ()) + tuple(
coercions.expect(roles.FromClauseRole, f) for f in fromclauses
)
+ return self
@HasMemoized.memoized_attribute
def selected_columns(self):
@@ -5959,6 +6023,9 @@ class Select(
return CompoundSelect._create_intersect_all(self, *other, **kwargs)
+SelfScalarSelect = typing.TypeVar("SelfScalarSelect", bound="ScalarSelect")
+
+
class ScalarSelect(roles.InElementRole, Generative, Grouping):
"""Represent a scalar subquery.
@@ -5998,18 +6065,19 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
c = columns
@_generative
- def where(self, crit):
+ def where(self: SelfScalarSelect, crit) -> SelfScalarSelect:
"""Apply a WHERE clause to the SELECT statement referred to
by this :class:`_expression.ScalarSelect`.
"""
self.element = self.element.where(crit)
+ return self
def self_group(self, **kwargs):
return self
@_generative
- def correlate(self, *fromclauses):
+ def correlate(self: SelfScalarSelect, *fromclauses) -> SelfScalarSelect:
r"""Return a new :class:`_expression.ScalarSelect`
which will correlate the given FROM
clauses to that of an enclosing :class:`_expression.Select`.
@@ -6039,9 +6107,12 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
"""
self.element = self.element.correlate(*fromclauses)
+ return self
@_generative
- def correlate_except(self, *fromclauses):
+ def correlate_except(
+ self: SelfScalarSelect, *fromclauses
+ ) -> SelfScalarSelect:
r"""Return a new :class:`_expression.ScalarSelect`
which will omit the given FROM
clauses from the auto-correlation process.
@@ -6073,6 +6144,7 @@ class ScalarSelect(roles.InElementRole, Generative, Grouping):
"""
self.element = self.element.correlate_except(*fromclauses)
+ return self
class Exists(UnaryExpression):
@@ -6228,6 +6300,9 @@ class Exists(UnaryExpression):
return e
+SelfTextualSelect = typing.TypeVar("SelfTextualSelect", bound="TextualSelect")
+
+
class TextualSelect(SelectBase):
"""Wrap a :class:`_expression.TextClause` construct within a
:class:`_expression.SelectBase`
@@ -6315,8 +6390,11 @@ class TextualSelect(SelectBase):
return self
@_generative
- def bindparams(self, *binds, **bind_as_values):
+ def bindparams(
+ self: SelfTextualSelect, *binds, **bind_as_values
+ ) -> SelfTextualSelect:
self.element = self.element.bindparams(*binds, **bind_as_values)
+ return self
def _generate_fromclause_column_proxies(self, fromclause):
fromclause._columns._populate_separate_keys(