main commit
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-10-16 16:30:25 +09:00
parent 91c7e04474
commit 537e7b363f
1146 changed files with 45926 additions and 77196 deletions

View File

@@ -1,12 +1,14 @@
# sql/base.py
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
# Copyright (C) 2005-2023 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
# mypy: allow-untyped-defs, allow-untyped-calls
"""Foundational utilities common to many sql modules."""
"""Foundational utilities common to many sql modules.
"""
from __future__ import annotations
@@ -22,7 +24,6 @@ from typing import Callable
from typing import cast
from typing import Dict
from typing import FrozenSet
from typing import Generator
from typing import Generic
from typing import Iterable
from typing import Iterator
@@ -56,7 +57,6 @@ from .. import util
from ..util import HasMemoized as HasMemoized
from ..util import hybridmethod
from ..util import typing as compat_typing
from ..util.typing import Final
from ..util.typing import Protocol
from ..util.typing import Self
from ..util.typing import TypeGuard
@@ -68,12 +68,11 @@ if TYPE_CHECKING:
from ._orm_types import DMLStrategyArgument
from ._orm_types import SynchronizeSessionArgument
from ._typing import _CLE
from .cache_key import CacheKey
from .compiler import SQLCompiler
from .elements import BindParameter
from .elements import ClauseList
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
@@ -82,7 +81,6 @@ if TYPE_CHECKING:
from .selectable import _JoinTargetElement
from .selectable import _SelectIterable
from .selectable import FromClause
from .visitors import anon_map
from ..engine import Connection
from ..engine import CursorResult
from ..engine.interfaces import _CoreMultiExecuteParams
@@ -110,7 +108,7 @@ class _NoArg(Enum):
return f"_NoArg.{self.name}"
NO_ARG: Final = _NoArg.NO_ARG
NO_ARG = _NoArg.NO_ARG
class _NoneName(Enum):
@@ -118,7 +116,7 @@ class _NoneName(Enum):
"""indicate a 'deferred' name that was ultimately the value None."""
_NONE_NAME: Final = _NoneName.NONE_NAME
_NONE_NAME = _NoneName.NONE_NAME
_T = TypeVar("_T", bound=Any)
@@ -153,18 +151,18 @@ class _DefaultDescriptionTuple(NamedTuple):
)
_never_select_column: operator.attrgetter[Any] = operator.attrgetter(
"_omit_from_statements"
)
_never_select_column = operator.attrgetter("_omit_from_statements")
class _EntityNamespace(Protocol):
def __getattr__(self, key: str) -> SQLCoreOperations[Any]: ...
def __getattr__(self, key: str) -> SQLCoreOperations[Any]:
...
class _HasEntityNamespace(Protocol):
@util.ro_non_memoized_property
def entity_namespace(self) -> _EntityNamespace: ...
def entity_namespace(self) -> _EntityNamespace:
...
def _is_has_entity_namespace(element: Any) -> TypeGuard[_HasEntityNamespace]:
@@ -190,12 +188,12 @@ class Immutable:
__slots__ = ()
_is_immutable: bool = True
_is_immutable = True
def unique_params(self, *optionaldict: Any, **kwargs: Any) -> NoReturn:
def unique_params(self, *optionaldict, **kwargs):
raise NotImplementedError("Immutable objects do not support copying")
def params(self, *optionaldict: Any, **kwargs: Any) -> NoReturn:
def params(self, *optionaldict, **kwargs):
raise NotImplementedError("Immutable objects do not support copying")
def _clone(self: _Self, **kw: Any) -> _Self:
@@ -210,7 +208,7 @@ class Immutable:
class SingletonConstant(Immutable):
"""Represent SQL constants like NULL, TRUE, FALSE"""
_is_singleton_constant: bool = True
_is_singleton_constant = True
_singleton: SingletonConstant
@@ -222,7 +220,7 @@ class SingletonConstant(Immutable):
raise NotImplementedError()
@classmethod
def _create_singleton(cls) -> None:
def _create_singleton(cls):
obj = object.__new__(cls)
obj.__init__() # type: ignore
@@ -263,7 +261,8 @@ _SelfGenerativeType = TypeVar("_SelfGenerativeType", bound="_GenerativeType")
class _GenerativeType(compat_typing.Protocol):
def _generate(self) -> Self: ...
def _generate(self) -> Self:
...
def _generative(fn: _Fn) -> _Fn:
@@ -291,17 +290,17 @@ def _generative(fn: _Fn) -> _Fn:
def _exclusive_against(*names: str, **kw: Any) -> Callable[[_Fn], _Fn]:
msgs: Dict[str, str] = kw.pop("msgs", {})
msgs = kw.pop("msgs", {})
defaults: Dict[str, str] = kw.pop("defaults", {})
defaults = kw.pop("defaults", {})
getters: List[Tuple[str, operator.attrgetter[Any], Optional[str]]] = [
getters = [
(name, operator.attrgetter(name), defaults.get(name, None))
for name in names
]
@util.decorator
def check(fn: _Fn, *args: Any, **kw: Any) -> Any:
def check(fn, *args, **kw):
# make pylance happy by not including "self" in the argument
# list
self = args[0]
@@ -350,16 +349,12 @@ def _cloned_intersection(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
The returned set is in terms of the entities present within 'a'.
"""
all_overlap: Set[_CLE] = set(_expand_cloned(a)).intersection(
_expand_cloned(b)
)
all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
return {elem for elem in a if all_overlap.intersection(elem._cloned_set)}
def _cloned_difference(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
all_overlap: Set[_CLE] = set(_expand_cloned(a)).intersection(
_expand_cloned(b)
)
all_overlap = set(_expand_cloned(a)).intersection(_expand_cloned(b))
return {
elem for elem in a if not all_overlap.intersection(elem._cloned_set)
}
@@ -371,12 +366,10 @@ class _DialectArgView(MutableMapping[str, Any]):
"""
__slots__ = ("obj",)
def __init__(self, obj: DialectKWArgs) -> None:
def __init__(self, obj):
self.obj = obj
def _key(self, key: str) -> Tuple[str, str]:
def _key(self, key):
try:
dialect, value_key = key.split("_", 1)
except ValueError as err:
@@ -384,7 +377,7 @@ class _DialectArgView(MutableMapping[str, Any]):
else:
return dialect, value_key
def __getitem__(self, key: str) -> Any:
def __getitem__(self, key):
dialect, value_key = self._key(key)
try:
@@ -394,7 +387,7 @@ class _DialectArgView(MutableMapping[str, Any]):
else:
return opt[value_key]
def __setitem__(self, key: str, value: Any) -> None:
def __setitem__(self, key, value):
try:
dialect, value_key = self._key(key)
except KeyError as err:
@@ -404,17 +397,17 @@ class _DialectArgView(MutableMapping[str, Any]):
else:
self.obj.dialect_options[dialect][value_key] = value
def __delitem__(self, key: str) -> None:
def __delitem__(self, key):
dialect, value_key = self._key(key)
del self.obj.dialect_options[dialect][value_key]
def __len__(self) -> int:
def __len__(self):
return sum(
len(args._non_defaults)
for args in self.obj.dialect_options.values()
)
def __iter__(self) -> Generator[str, None, None]:
def __iter__(self):
return (
"%s_%s" % (dialect_name, value_name)
for dialect_name in self.obj.dialect_options
@@ -433,31 +426,31 @@ class _DialectArgDict(MutableMapping[str, Any]):
"""
def __init__(self) -> None:
self._non_defaults: Dict[str, Any] = {}
self._defaults: Dict[str, Any] = {}
def __init__(self):
self._non_defaults = {}
self._defaults = {}
def __len__(self) -> int:
def __len__(self):
return len(set(self._non_defaults).union(self._defaults))
def __iter__(self) -> Iterator[str]:
def __iter__(self):
return iter(set(self._non_defaults).union(self._defaults))
def __getitem__(self, key: str) -> Any:
def __getitem__(self, key):
if key in self._non_defaults:
return self._non_defaults[key]
else:
return self._defaults[key]
def __setitem__(self, key: str, value: Any) -> None:
def __setitem__(self, key, value):
self._non_defaults[key] = value
def __delitem__(self, key: str) -> None:
def __delitem__(self, key):
del self._non_defaults[key]
@util.preload_module("sqlalchemy.dialects")
def _kw_reg_for_dialect(dialect_name: str) -> Optional[Dict[Any, Any]]:
def _kw_reg_for_dialect(dialect_name):
dialect_cls = util.preloaded.dialects.registry.load(dialect_name)
if dialect_cls.construct_arguments is None:
return None
@@ -479,21 +472,19 @@ class DialectKWArgs:
__slots__ = ()
_dialect_kwargs_traverse_internals: List[Tuple[str, Any]] = [
_dialect_kwargs_traverse_internals = [
("dialect_options", InternalTraversal.dp_dialect_options)
]
@classmethod
def argument_for(
cls, dialect_name: str, argument_name: str, default: Any
) -> None:
def argument_for(cls, dialect_name, argument_name, default):
"""Add a new kind of dialect-specific keyword argument for this class.
E.g.::
Index.argument_for("mydialect", "length", None)
some_index = Index("a", "b", mydialect_length=5)
some_index = Index('a', 'b', mydialect_length=5)
The :meth:`.DialectKWArgs.argument_for` method is a per-argument
way adding extra arguments to the
@@ -523,9 +514,7 @@ class DialectKWArgs:
"""
construct_arg_dictionary: Optional[Dict[Any, Any]] = (
DialectKWArgs._kw_registry[dialect_name]
)
construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
if construct_arg_dictionary is None:
raise exc.ArgumentError(
"Dialect '%s' does have keyword-argument "
@@ -535,8 +524,8 @@ class DialectKWArgs:
construct_arg_dictionary[cls] = {}
construct_arg_dictionary[cls][argument_name] = default
@property
def dialect_kwargs(self) -> _DialectArgView:
@util.memoized_property
def dialect_kwargs(self):
"""A collection of keyword arguments specified as dialect-specific
options to this construct.
@@ -557,29 +546,26 @@ class DialectKWArgs:
return _DialectArgView(self)
@property
def kwargs(self) -> _DialectArgView:
def kwargs(self):
"""A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
return self.dialect_kwargs
_kw_registry: util.PopulateDict[str, Optional[Dict[Any, Any]]] = (
util.PopulateDict(_kw_reg_for_dialect)
)
_kw_registry = util.PopulateDict(_kw_reg_for_dialect)
@classmethod
def _kw_reg_for_dialect_cls(cls, dialect_name: str) -> _DialectArgDict:
def _kw_reg_for_dialect_cls(self, dialect_name):
construct_arg_dictionary = DialectKWArgs._kw_registry[dialect_name]
d = _DialectArgDict()
if construct_arg_dictionary is None:
d._defaults.update({"*": None})
else:
for cls in reversed(cls.__mro__):
for cls in reversed(self.__class__.__mro__):
if cls in construct_arg_dictionary:
d._defaults.update(construct_arg_dictionary[cls])
return d
@util.memoized_property
def dialect_options(self) -> util.PopulateDict[str, _DialectArgDict]:
def dialect_options(self):
"""A collection of keyword arguments specified as dialect-specific
options to this construct.
@@ -587,7 +573,7 @@ class DialectKWArgs:
and ``<argument_name>``. For example, the ``postgresql_where``
argument would be locatable as::
arg = my_object.dialect_options["postgresql"]["where"]
arg = my_object.dialect_options['postgresql']['where']
.. versionadded:: 0.9.2
@@ -597,7 +583,9 @@ class DialectKWArgs:
"""
return util.PopulateDict(self._kw_reg_for_dialect_cls)
return util.PopulateDict(
util.portable_instancemethod(self._kw_reg_for_dialect_cls)
)
def _validate_dialect_kwargs(self, kwargs: Dict[str, Any]) -> None:
# validate remaining kwargs that they all specify DB prefixes
@@ -673,9 +661,7 @@ class CompileState:
_ambiguous_table_name_map: Optional[_AmbiguousTableNameMap]
@classmethod
def create_for_statement(
cls, statement: Executable, compiler: SQLCompiler, **kw: Any
) -> CompileState:
def create_for_statement(cls, statement, compiler, **kw):
# factory construction.
if statement._propagate_attrs:
@@ -815,11 +801,14 @@ class _MetaOptions(type):
if TYPE_CHECKING:
def __getattr__(self, key: str) -> Any: ...
def __getattr__(self, key: str) -> Any:
...
def __setattr__(self, key: str, value: Any) -> None: ...
def __setattr__(self, key: str, value: Any) -> None:
...
def __delattr__(self, key: str) -> None: ...
def __delattr__(self, key: str) -> None:
...
class Options(metaclass=_MetaOptions):
@@ -841,7 +830,7 @@ class Options(metaclass=_MetaOptions):
)
super().__init_subclass__()
def __init__(self, **kw: Any) -> None:
def __init__(self, **kw):
self.__dict__.update(kw)
def __add__(self, other):
@@ -866,7 +855,7 @@ class Options(metaclass=_MetaOptions):
return False
return True
def __repr__(self) -> str:
def __repr__(self):
# TODO: fairly inefficient, used only in debugging right now.
return "%s(%s)" % (
@@ -883,7 +872,7 @@ class Options(metaclass=_MetaOptions):
return issubclass(cls, klass)
@hybridmethod
def add_to_element(self, name: str, value: str) -> Any:
def add_to_element(self, name, value):
return self + {name: getattr(self, name) + value}
@hybridmethod
@@ -897,7 +886,7 @@ class Options(metaclass=_MetaOptions):
return cls._state_dict_const
@classmethod
def safe_merge(cls, other: "Options") -> Any:
def safe_merge(cls, other):
d = other._state_dict()
# only support a merge with another object of our class
@@ -923,12 +912,8 @@ class Options(metaclass=_MetaOptions):
@classmethod
def from_execution_options(
cls,
key: str,
attrs: set[str],
exec_options: Mapping[str, Any],
statement_exec_options: Mapping[str, Any],
) -> Tuple["Options", Mapping[str, Any]]:
cls, key, attrs, exec_options, statement_exec_options
):
"""process Options argument in terms of execution options.
@@ -939,7 +924,11 @@ class Options(metaclass=_MetaOptions):
execution_options,
) = QueryContext.default_load_options.from_execution_options(
"_sa_orm_load_options",
{"populate_existing", "autoflush", "yield_per"},
{
"populate_existing",
"autoflush",
"yield_per"
},
execution_options,
statement._execution_options,
)
@@ -967,8 +956,8 @@ class Options(metaclass=_MetaOptions):
result[local] = statement_exec_options[argname]
new_options = existing_options + result
exec_options = util.immutabledict(exec_options).merge_with(
{key: new_options}
exec_options = util.immutabledict().merge_with(
exec_options, {key: new_options}
)
return new_options, exec_options
@@ -977,43 +966,42 @@ class Options(metaclass=_MetaOptions):
if TYPE_CHECKING:
def __getattr__(self, key: str) -> Any: ...
def __getattr__(self, key: str) -> Any:
...
def __setattr__(self, key: str, value: Any) -> None: ...
def __setattr__(self, key: str, value: Any) -> None:
...
def __delattr__(self, key: str) -> None: ...
def __delattr__(self, key: str) -> None:
...
class CacheableOptions(Options, HasCacheKey):
__slots__ = ()
@hybridmethod
def _gen_cache_key_inst(
self, anon_map: Any, bindparams: List[BindParameter[Any]]
) -> Optional[Tuple[Any]]:
def _gen_cache_key_inst(self, anon_map, bindparams):
return HasCacheKey._gen_cache_key(self, anon_map, bindparams)
@_gen_cache_key_inst.classlevel
def _gen_cache_key(
cls, anon_map: "anon_map", bindparams: List[BindParameter[Any]]
) -> Tuple[CacheableOptions, Any]:
def _gen_cache_key(cls, anon_map, bindparams):
return (cls, ())
@hybridmethod
def _generate_cache_key(self) -> Optional[CacheKey]:
def _generate_cache_key(self):
return HasCacheKey._generate_cache_key_for_object(self)
class ExecutableOption(HasCopyInternals):
__slots__ = ()
_annotations: _ImmutableExecuteOptions = util.EMPTY_DICT
_annotations = util.EMPTY_DICT
__visit_name__: str = "executable_option"
__visit_name__ = "executable_option"
_is_has_cache_key: bool = False
_is_has_cache_key = False
_is_core: bool = True
_is_core = True
def _clone(self, **kw):
"""Create a shallow copy of this ExecutableOption."""
@@ -1033,7 +1021,7 @@ class Executable(roles.StatementRole):
supports_execution: bool = True
_execution_options: _ImmutableExecuteOptions = util.EMPTY_DICT
_is_default_generator: bool = False
_is_default_generator = False
_with_options: Tuple[ExecutableOption, ...] = ()
_with_context_options: Tuple[
Tuple[Callable[[CompileState], None], Any], ...
@@ -1049,13 +1037,12 @@ class Executable(roles.StatementRole):
("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs),
]
is_select: bool = False
is_from_statement: bool = False
is_update: bool = False
is_insert: bool = False
is_text: bool = False
is_delete: bool = False
is_dml: bool = False
is_select = False
is_update = False
is_insert = False
is_text = False
is_delete = False
is_dml = False
if TYPE_CHECKING:
__visit_name__: str
@@ -1071,24 +1058,27 @@ class Executable(roles.StatementRole):
**kw: Any,
) -> Tuple[
Compiled, Optional[Sequence[BindParameter[Any]]], CacheStats
]: ...
]:
...
def _execute_on_connection(
self,
connection: Connection,
distilled_params: _CoreMultiExecuteParams,
execution_options: CoreExecuteOptionsParameter,
) -> CursorResult[Any]: ...
) -> CursorResult[Any]:
...
def _execute_on_scalar(
self,
connection: Connection,
distilled_params: _CoreMultiExecuteParams,
execution_options: CoreExecuteOptionsParameter,
) -> Any: ...
) -> Any:
...
@util.ro_non_memoized_property
def _all_selected_columns(self) -> _SelectIterable:
def _all_selected_columns(self):
raise NotImplementedError()
@property
@@ -1189,12 +1179,13 @@ class Executable(roles.StatementRole):
render_nulls: bool = ...,
is_delete_using: bool = ...,
is_update_from: bool = ...,
preserve_rowcount: bool = False,
**opt: Any,
) -> Self: ...
) -> Self:
...
@overload
def execution_options(self, **opt: Any) -> Self: ...
def execution_options(self, **opt: Any) -> Self:
...
@_generative
def execution_options(self, **kw: Any) -> Self:
@@ -1246,7 +1237,6 @@ class Executable(roles.StatementRole):
from sqlalchemy import event
@event.listens_for(some_engine, "before_execute")
def _process_opt(conn, statement, multiparams, params, execution_options):
"run a SQL function before invoking a statement"
@@ -1348,21 +1338,10 @@ class SchemaEventTarget(event.EventTarget):
self.dispatch.after_parent_attach(self, parent)
class SchemaVisitable(SchemaEventTarget, visitors.Visitable):
"""Base class for elements that are targets of a :class:`.SchemaVisitor`.
.. versionadded:: 2.0.41
"""
class SchemaVisitor(ClauseVisitor):
"""Define the visiting for ``SchemaItem`` and more
generally ``SchemaVisitable`` objects.
"""Define the visiting for ``SchemaItem`` objects."""
"""
__traverse_options__: Dict[str, Any] = {"schema_visitor": True}
__traverse_options__ = {"schema_visitor": True}
class _SentinelDefaultCharacterization(Enum):
@@ -1387,7 +1366,7 @@ class _SentinelColumnCharacterization(NamedTuple):
_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 _ColumnMetrics(Generic[_COL_co]):
@@ -1397,7 +1376,7 @@ class _ColumnMetrics(Generic[_COL_co]):
def __init__(
self, collection: ColumnCollection[Any, _COL_co], col: _COL_co
) -> None:
):
self.column = col
# proxy_index being non-empty means it was initialized.
@@ -1407,10 +1386,10 @@ class _ColumnMetrics(Generic[_COL_co]):
for eps_col in col._expanded_proxy_set:
pi[eps_col].add(self)
def get_expanded_proxy_set(self) -> FrozenSet[ColumnElement[Any]]:
def get_expanded_proxy_set(self):
return self.column._expanded_proxy_set
def dispose(self, collection: ColumnCollection[_COLKEY, _COL_co]) -> None:
def dispose(self, collection):
pi = collection._proxy_index
if not pi:
return
@@ -1509,14 +1488,14 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
mean either two columns with the same key, in which case the column
returned by key access is **arbitrary**::
>>> x1, x2 = Column("x", Integer), Column("x", Integer)
>>> x1, x2 = Column('x', Integer), Column('x', Integer)
>>> cc = ColumnCollection(columns=[(x1.name, x1), (x2.name, x2)])
>>> list(cc)
[Column('x', Integer(), table=None),
Column('x', Integer(), table=None)]
>>> cc["x"] is x1
>>> cc['x'] is x1
False
>>> cc["x"] is x2
>>> cc['x'] is x2
True
Or it can also mean the same column multiple times. These cases are
@@ -1543,7 +1522,7 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
"""
__slots__ = ("_collection", "_index", "_colset", "_proxy_index")
__slots__ = "_collection", "_index", "_colset", "_proxy_index"
_collection: List[Tuple[_COLKEY, _COL_co, _ColumnMetrics[_COL_co]]]
_index: Dict[Union[None, str, int], Tuple[_COLKEY, _COL_co]]
@@ -1612,17 +1591,20 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
return iter([col for _, col, _ in self._collection])
@overload
def __getitem__(self, key: Union[str, int]) -> _COL_co: ...
def __getitem__(self, key: Union[str, int]) -> _COL_co:
...
@overload
def __getitem__(
self, key: Tuple[Union[str, int], ...]
) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]: ...
) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]:
...
@overload
def __getitem__(
self, key: slice
) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]: ...
) -> ReadOnlyColumnCollection[_COLKEY, _COL_co]:
...
def __getitem__(
self, key: Union[str, int, slice, Tuple[Union[str, int], ...]]
@@ -1662,7 +1644,7 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
else:
return True
def compare(self, other: ColumnCollection[_COLKEY, _COL_co]) -> bool:
def compare(self, other: ColumnCollection[Any, Any]) -> bool:
"""Compare this :class:`_expression.ColumnCollection` to another
based on the names of the keys"""
@@ -1675,15 +1657,9 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
def __eq__(self, other: Any) -> bool:
return self.compare(other)
@overload
def get(self, key: str, default: None = None) -> Optional[_COL_co]: ...
@overload
def get(self, key: str, default: _COL) -> Union[_COL_co, _COL]: ...
def get(
self, key: str, default: Optional[_COL] = None
) -> Optional[Union[_COL_co, _COL]]:
self, key: str, default: Optional[_COL_co] = None
) -> Optional[_COL_co]:
"""Get a :class:`_sql.ColumnClause` or :class:`_schema.Column` object
based on a string key name from this
:class:`_expression.ColumnCollection`."""
@@ -1713,7 +1689,7 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
:class:`_sql.ColumnCollection`."""
raise NotImplementedError()
def remove(self, column: Any) -> NoReturn:
def remove(self, column: Any) -> None:
raise NotImplementedError()
def update(self, iter_: Any) -> NoReturn:
@@ -1722,7 +1698,7 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
raise NotImplementedError()
# https://github.com/python/mypy/issues/4266
__hash__: Optional[int] = None # type: ignore
__hash__ = None # type: ignore
def _populate_separate_keys(
self, iter_: Iterable[Tuple[_COLKEY, _COL_co]]
@@ -1815,7 +1791,7 @@ class ColumnCollection(Generic[_COLKEY, _COL_co]):
return ReadOnlyColumnCollection(self)
def _init_proxy_index(self) -> None:
def _init_proxy_index(self):
"""populate the "proxy index", if empty.
proxy index is added in 2.0 to provide more efficient operation
@@ -1964,15 +1940,16 @@ class DedupeColumnCollection(ColumnCollection[str, _NAMEDCOL]):
"""
def add( # type: ignore[override]
self, column: _NAMEDCOL, key: Optional[str] = None
def add(
self, column: ColumnElement[Any], key: Optional[str] = None
) -> None:
if key is not None and column.key != key:
named_column = cast(_NAMEDCOL, column)
if key is not None and named_column.key != key:
raise exc.ArgumentError(
"DedupeColumnCollection requires columns be under "
"the same key as their .key"
)
key = column.key
key = named_column.key
if key is None:
raise exc.ArgumentError(
@@ -1982,17 +1959,17 @@ class DedupeColumnCollection(ColumnCollection[str, _NAMEDCOL]):
if key in self._index:
existing = self._index[key][1]
if existing is column:
if existing is named_column:
return
self.replace(column)
self.replace(named_column)
# pop out memoized proxy_set as this
# operation may very well be occurring
# in a _make_proxy operation
util.memoized_property.reset(column, "proxy_set")
util.memoized_property.reset(named_column, "proxy_set")
else:
self._append_new_column(key, column)
self._append_new_column(key, named_column)
def _append_new_column(self, key: str, named_column: _NAMEDCOL) -> None:
l = len(self._collection)
@@ -2034,7 +2011,7 @@ class DedupeColumnCollection(ColumnCollection[str, _NAMEDCOL]):
def extend(self, iter_: Iterable[_NAMEDCOL]) -> None:
self._populate_separate_keys((col.key, col) for col in iter_)
def remove(self, column: _NAMEDCOL) -> None: # type: ignore[override]
def remove(self, column: _NAMEDCOL) -> None:
if column not in self._colset:
raise ValueError(
"Can't remove column %r; column is not in this collection"
@@ -2067,8 +2044,8 @@ class DedupeColumnCollection(ColumnCollection[str, _NAMEDCOL]):
e.g.::
t = Table("sometable", metadata, Column("col1", Integer))
t.columns.replace(Column("col1", Integer, key="columnone"))
t = Table('sometable', metadata, Column('col1', Integer))
t.columns.replace(Column('col1', Integer, key='columnone'))
will remove the original 'col1' from the collection, and add
the new column under the name 'columnname'.
@@ -2131,17 +2108,17 @@ class ReadOnlyColumnCollection(
):
__slots__ = ("_parent",)
def __init__(self, collection: ColumnCollection[_COLKEY, _COL_co]):
def __init__(self, collection):
object.__setattr__(self, "_parent", collection)
object.__setattr__(self, "_colset", collection._colset)
object.__setattr__(self, "_index", collection._index)
object.__setattr__(self, "_collection", collection._collection)
object.__setattr__(self, "_proxy_index", collection._proxy_index)
def __getstate__(self) -> Dict[str, _COL_co]:
def __getstate__(self):
return {"_parent": self._parent}
def __setstate__(self, state: Dict[str, Any]) -> None:
def __setstate__(self, state):
parent = state["_parent"]
self.__init__(parent) # type: ignore
@@ -2156,10 +2133,10 @@ class ReadOnlyColumnCollection(
class ColumnSet(util.OrderedSet["ColumnClause[Any]"]):
def contains_column(self, col: ColumnClause[Any]) -> bool:
def contains_column(self, col):
return col in self
def extend(self, cols: Iterable[Any]) -> None:
def extend(self, cols):
for col in cols:
self.add(col)
@@ -2171,12 +2148,12 @@ class ColumnSet(util.OrderedSet["ColumnClause[Any]"]):
l.append(c == local)
return elements.and_(*l)
def __hash__(self) -> int: # type: ignore[override]
def __hash__(self):
return hash(tuple(x for x in self))
def _entity_namespace(
entity: Union[_HasEntityNamespace, ExternallyTraversible],
entity: Union[_HasEntityNamespace, ExternallyTraversible]
) -> _EntityNamespace:
"""Return the nearest .entity_namespace for the given entity.