summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2022-02-13 20:37:12 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2022-02-13 20:37:12 +0000
commitd6b3c82b0c329730bcaff42b4bb39dba83acb536 (patch)
treed6b7f744a35c8d89615eeb0504ee7a4193f95642 /lib/sqlalchemy/sql
parent260ade78a70d51378de9e7b9456bfe6218859b6c (diff)
parente545298e35ea9f126054b337e4b5ba01988b29f7 (diff)
downloadsqlalchemy-d6b3c82b0c329730bcaff42b4bb39dba83acb536.tar.gz
Merge "establish mypy / typing approach for v2.0" into main
Diffstat (limited to 'lib/sqlalchemy/sql')
-rw-r--r--lib/sqlalchemy/sql/__init__.py1
-rw-r--r--lib/sqlalchemy/sql/_selectable_constructors.py8
-rw-r--r--lib/sqlalchemy/sql/_typing.py12
-rw-r--r--lib/sqlalchemy/sql/base.py6
-rw-r--r--lib/sqlalchemy/sql/compiler.py2
-rw-r--r--lib/sqlalchemy/sql/ddl.py20
-rw-r--r--lib/sqlalchemy/sql/expression.py1
-rw-r--r--lib/sqlalchemy/sql/naming.py2
-rw-r--r--lib/sqlalchemy/sql/roles.py29
-rw-r--r--lib/sqlalchemy/sql/schema.py744
-rw-r--r--lib/sqlalchemy/sql/selectable.py67
-rw-r--r--lib/sqlalchemy/sql/type_api.py34
12 files changed, 518 insertions, 408 deletions
diff --git a/lib/sqlalchemy/sql/__init__.py b/lib/sqlalchemy/sql/__init__.py
index 2f84370aa..169ddf3db 100644
--- a/lib/sqlalchemy/sql/__init__.py
+++ b/lib/sqlalchemy/sql/__init__.py
@@ -75,6 +75,7 @@ from .expression import quoted_name as quoted_name
from .expression import Select as Select
from .expression import select as select
from .expression import Selectable as Selectable
+from .expression import SelectLabelStyle as SelectLabelStyle
from .expression import StatementLambdaElement as StatementLambdaElement
from .expression import Subquery as Subquery
from .expression import table as table
diff --git a/lib/sqlalchemy/sql/_selectable_constructors.py b/lib/sqlalchemy/sql/_selectable_constructors.py
index 4b67c12f0..d3cf207da 100644
--- a/lib/sqlalchemy/sql/_selectable_constructors.py
+++ b/lib/sqlalchemy/sql/_selectable_constructors.py
@@ -6,11 +6,11 @@
# the MIT License: https://www.opensource.org/licenses/mit-license.php
from typing import Any
-from typing import Type
from typing import Union
from . import coercions
from . import roles
+from ._typing import _ColumnsClauseElement
from .elements import ColumnClause
from .selectable import Alias
from .selectable import CompoundSelect
@@ -21,6 +21,8 @@ from .selectable import Select
from .selectable import TableClause
from .selectable import TableSample
from .selectable import Values
+from ..util.typing import _LiteralStar
+from ..util.typing import Literal
def alias(selectable, name=None, flat=False):
@@ -279,7 +281,9 @@ def outerjoin(left, right, onclause=None, full=False):
return Join(left, right, onclause, isouter=True, full=full)
-def select(*entities: Union[roles.ColumnsClauseRole, Type]) -> "Select":
+def select(
+ *entities: Union[_LiteralStar, Literal[1], _ColumnsClauseElement]
+) -> "Select":
r"""Construct a new :class:`_expression.Select`.
diff --git a/lib/sqlalchemy/sql/_typing.py b/lib/sqlalchemy/sql/_typing.py
index b5b0efb21..4d2dd2688 100644
--- a/lib/sqlalchemy/sql/_typing.py
+++ b/lib/sqlalchemy/sql/_typing.py
@@ -1,9 +1,21 @@
from typing import Any
from typing import Mapping
from typing import Sequence
+from typing import Type
from typing import Union
+from . import roles
+from ..inspection import Inspectable
+from ..util import immutabledict
+
_SingleExecuteParams = Mapping[str, Any]
_MultiExecuteParams = Sequence[_SingleExecuteParams]
_ExecuteParams = Union[_SingleExecuteParams, _MultiExecuteParams]
_ExecuteOptions = Mapping[str, Any]
+_ImmutableExecuteOptions = immutabledict[str, Any]
+_ColumnsClauseElement = Union[
+ roles.ColumnsClauseRole, Type, Inspectable[roles.HasClauseElement]
+]
+_FromClauseElement = Union[
+ roles.FromClauseRole, Type, Inspectable[roles.HasFromClauseElement]
+]
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index f4fe7afab..5828f9369 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -21,6 +21,7 @@ from typing import TypeVar
from . import roles
from . import visitors
+from ._typing import _ImmutableExecuteOptions
from .cache_key import HasCacheKey # noqa
from .cache_key import MemoizedHasCacheKey # noqa
from .traversals import HasCopyInternals # noqa
@@ -832,9 +833,8 @@ class Executable(roles.StatementRole, Generative):
"""
- supports_execution = True
- _execution_options = util.immutabledict()
- _bind = None
+ supports_execution: bool = True
+ _execution_options: _ImmutableExecuteOptions = util.immutabledict()
_with_options = ()
_with_context_options = ()
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 9cf4d8397..bf78b4231 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -889,7 +889,7 @@ class SQLCompiler(Compiled):
def _apply_numbered_params(self):
poscount = itertools.count(1)
self.string = re.sub(
- r"\[_POSITION\]", lambda m: str(util.next(poscount)), self.string
+ r"\[_POSITION\]", lambda m: str(next(poscount)), self.string
)
@util.memoized_property
diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py
index 18931ce67..f622023b0 100644
--- a/lib/sqlalchemy/sql/ddl.py
+++ b/lib/sqlalchemy/sql/ddl.py
@@ -10,6 +10,11 @@ to invoke them for a create/drop call.
"""
import typing
+from typing import Callable
+from typing import List
+from typing import Optional
+from typing import Sequence
+from typing import Tuple
from . import roles
from .base import _generative
@@ -21,6 +26,11 @@ from .. import util
from ..util import topological
+if typing.TYPE_CHECKING:
+ from .schema import ForeignKeyConstraint
+ from .schema import Table
+
+
class _DDLCompiles(ClauseElement):
_hierarchy_supports_caching = False
"""disable cache warnings for all _DDLCompiles subclasses. """
@@ -1007,10 +1017,10 @@ class SchemaDropper(DDLBase):
def sort_tables(
- tables,
- skip_fn=None,
- extra_dependencies=None,
-):
+ tables: Sequence["Table"],
+ skip_fn: Optional[Callable[["ForeignKeyConstraint"], bool]] = None,
+ extra_dependencies: Optional[Sequence[Tuple["Table", "Table"]]] = None,
+) -> List["Table"]:
"""Sort a collection of :class:`_schema.Table` objects based on
dependency.
@@ -1051,7 +1061,7 @@ def sort_tables(
:param tables: a sequence of :class:`_schema.Table` objects.
:param skip_fn: optional callable which will be passed a
- :class:`_schema.ForeignKey` object; if it returns True, this
+ :class:`_schema.ForeignKeyConstraint` object; if it returns True, this
constraint will not be considered as a dependency. Note this is
**different** from the same parameter in
:func:`.sort_tables_and_constraints`, which is
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 0ed5bd986..22195cd7c 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -136,6 +136,7 @@ from .selectable import ScalarSelect as ScalarSelect
from .selectable import Select as Select
from .selectable import Selectable as Selectable
from .selectable import SelectBase as SelectBase
+from .selectable import SelectLabelStyle as SelectLabelStyle
from .selectable import Subquery as Subquery
from .selectable import TableClause as TableClause
from .selectable import TableSample as TableSample
diff --git a/lib/sqlalchemy/sql/naming.py b/lib/sqlalchemy/sql/naming.py
index 00a2b1d89..15a1566a6 100644
--- a/lib/sqlalchemy/sql/naming.py
+++ b/lib/sqlalchemy/sql/naming.py
@@ -14,7 +14,7 @@ import re
from . import events # noqa
from .elements import _NONE_NAME
-from .elements import conv
+from .elements import conv as conv
from .schema import CheckConstraint
from .schema import Column
from .schema import Constraint
diff --git a/lib/sqlalchemy/sql/roles.py b/lib/sqlalchemy/sql/roles.py
index 787a1c25e..b41ef7a5d 100644
--- a/lib/sqlalchemy/sql/roles.py
+++ b/lib/sqlalchemy/sql/roles.py
@@ -4,10 +4,17 @@
#
# This module is part of SQLAlchemy and is released under
# the MIT License: https://www.opensource.org/licenses/mit-license.php
+import typing
+from sqlalchemy.util.langhelpers import TypingOnly
from .. import util
+if typing.TYPE_CHECKING:
+ from .elements import ClauseElement
+ from .selectable import FromClause
+
+
class SQLRole:
"""Define a "role" within a SQL statement structure.
@@ -284,3 +291,25 @@ class DDLReferredColumnRole(DDLConstraintColumnRole):
_role_name = (
"String column name or Column object for DDL foreign key constraint"
)
+
+
+class HasClauseElement(TypingOnly):
+ """indicates a class that has a __clause_element__() method"""
+
+ __slots__ = ()
+
+ if typing.TYPE_CHECKING:
+
+ def __clause_element__(self) -> "ClauseElement":
+ ...
+
+
+class HasFromClauseElement(HasClauseElement, TypingOnly):
+ """indicates a class that has a __clause_element__() method"""
+
+ __slots__ = ()
+
+ if typing.TYPE_CHECKING:
+
+ def __clause_element__(self) -> "FromClause":
+ ...
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index a04fad05d..9387ae030 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -31,9 +31,12 @@ as components in SQL expressions.
import collections
import typing
from typing import Any
+from typing import Dict
+from typing import List
from typing import MutableMapping
from typing import Optional
from typing import overload
+from typing import Sequence as _typing_Sequence
from typing import Type
from typing import TypeVar
from typing import Union
@@ -52,6 +55,7 @@ from .elements import ClauseElement
from .elements import ColumnClause
from .elements import ColumnElement
from .elements import quoted_name
+from .elements import SQLCoreOperations
from .elements import TextClause
from .selectable import TableClause
from .type_api import to_instance
@@ -64,9 +68,12 @@ from ..util.typing import Literal
if typing.TYPE_CHECKING:
from .type_api import TypeEngine
+ from ..engine import Connection
+ from ..engine import Engine
_T = TypeVar("_T", bound="Any")
_ServerDefaultType = Union["FetchedValue", str, TextClause, ColumnElement]
+_TAB = TypeVar("_TAB", bound="Table")
RETAIN_SCHEMA = util.symbol("retain_schema")
@@ -188,313 +195,6 @@ class Table(DialectKWArgs, SchemaItem, TableClause):
:ref:`metadata_describing` - Introduction to database metadata
- Constructor arguments are as follows:
-
- :param name: The name of this table as represented in the database.
-
- The table name, along with the value of the ``schema`` parameter,
- forms a key which uniquely identifies this :class:`_schema.Table`
- within
- the owning :class:`_schema.MetaData` collection.
- Additional calls to :class:`_schema.Table` with the same name,
- metadata,
- and schema name will return the same :class:`_schema.Table` object.
-
- Names which contain no upper case characters
- will be treated as case insensitive names, and will not be quoted
- unless they are a reserved word or contain special characters.
- A name with any number of upper case characters is considered
- to be case sensitive, and will be sent as quoted.
-
- To enable unconditional quoting for the table name, specify the flag
- ``quote=True`` to the constructor, or use the :class:`.quoted_name`
- construct to specify the name.
-
- :param metadata: a :class:`_schema.MetaData`
- object which will contain this
- table. The metadata is used as a point of association of this table
- with other tables which are referenced via foreign key. It also
- may be used to associate this table with a particular
- :class:`.Connection` or :class:`.Engine`.
-
- :param \*args: Additional positional arguments are used primarily
- to add the list of :class:`_schema.Column`
- objects contained within this
- table. Similar to the style of a CREATE TABLE statement, other
- :class:`.SchemaItem` constructs may be added here, including
- :class:`.PrimaryKeyConstraint`, and
- :class:`_schema.ForeignKeyConstraint`.
-
- :param autoload: Defaults to ``False``, unless
- :paramref:`_schema.Table.autoload_with`
- is set in which case it defaults to ``True``;
- :class:`_schema.Column` objects
- for this table should be reflected from the database, possibly
- augmenting objects that were explicitly specified.
- :class:`_schema.Column` and other objects explicitly set on the
- table will replace corresponding reflected objects.
-
- .. deprecated:: 1.4
-
- The autoload parameter is deprecated and will be removed in
- version 2.0. Please use the
- :paramref:`_schema.Table.autoload_with` parameter, passing an
- engine or connection.
-
- .. seealso::
-
- :ref:`metadata_reflection_toplevel`
-
- :param autoload_replace: Defaults to ``True``; when using
- :paramref:`_schema.Table.autoload`
- in conjunction with :paramref:`_schema.Table.extend_existing`,
- indicates
- that :class:`_schema.Column` objects present in the already-existing
- :class:`_schema.Table`
- object should be replaced with columns of the same
- name retrieved from the autoload process. When ``False``, columns
- already present under existing names will be omitted from the
- reflection process.
-
- Note that this setting does not impact :class:`_schema.Column` objects
- specified programmatically within the call to :class:`_schema.Table`
- that
- also is autoloading; those :class:`_schema.Column` objects will always
- replace existing columns of the same name when
- :paramref:`_schema.Table.extend_existing` is ``True``.
-
- .. seealso::
-
- :paramref:`_schema.Table.autoload`
-
- :paramref:`_schema.Table.extend_existing`
-
- :param autoload_with: An :class:`_engine.Engine` or
- :class:`_engine.Connection` object,
- or a :class:`_reflection.Inspector` object as returned by
- :func:`_sa.inspect`
- against one, with which this :class:`_schema.Table`
- object will be reflected.
- When set to a non-None value, the autoload process will take place
- for this table against the given engine or connection.
-
- :param extend_existing: When ``True``, indicates that if this
- :class:`_schema.Table` is already present in the given
- :class:`_schema.MetaData`,
- apply further arguments within the constructor to the existing
- :class:`_schema.Table`.
-
- If :paramref:`_schema.Table.extend_existing` or
- :paramref:`_schema.Table.keep_existing` are not set,
- and the given name
- of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
- that is
- already present in the target :class:`_schema.MetaData` collection,
- and
- this :class:`_schema.Table`
- specifies additional columns or other constructs
- or flags that modify the table's state, an
- error is raised. The purpose of these two mutually-exclusive flags
- is to specify what action should be taken when a
- :class:`_schema.Table`
- is specified that matches an existing :class:`_schema.Table`,
- yet specifies
- additional constructs.
-
- :paramref:`_schema.Table.extend_existing`
- will also work in conjunction
- with :paramref:`_schema.Table.autoload` to run a new reflection
- operation against the database, even if a :class:`_schema.Table`
- of the same name is already present in the target
- :class:`_schema.MetaData`; newly reflected :class:`_schema.Column`
- objects
- and other options will be added into the state of the
- :class:`_schema.Table`, potentially overwriting existing columns
- and options of the same name.
-
- As is always the case with :paramref:`_schema.Table.autoload`,
- :class:`_schema.Column` objects can be specified in the same
- :class:`_schema.Table`
- constructor, which will take precedence. Below, the existing
- table ``mytable`` will be augmented with :class:`_schema.Column`
- objects
- both reflected from the database, as well as the given
- :class:`_schema.Column`
- named "y"::
-
- Table("mytable", metadata,
- Column('y', Integer),
- extend_existing=True,
- autoload_with=engine
- )
-
- .. seealso::
-
- :paramref:`_schema.Table.autoload`
-
- :paramref:`_schema.Table.autoload_replace`
-
- :paramref:`_schema.Table.keep_existing`
-
-
- :param implicit_returning: True by default - indicates that
- RETURNING can be used by default to fetch newly inserted primary key
- values, for backends which support this. Note that
- :func:`_sa.create_engine` also provides an ``implicit_returning``
- flag.
-
- :param include_columns: A list of strings indicating a subset of
- columns to be loaded via the ``autoload`` operation; table columns who
- aren't present in this list will not be represented on the resulting
- ``Table`` object. Defaults to ``None`` which indicates all columns
- should be reflected.
-
- :param resolve_fks: Whether or not to reflect :class:`_schema.Table`
- objects
- related to this one via :class:`_schema.ForeignKey` objects, when
- :paramref:`_schema.Table.autoload` or
- :paramref:`_schema.Table.autoload_with` is
- specified. Defaults to True. Set to False to disable reflection of
- related tables as :class:`_schema.ForeignKey`
- objects are encountered; may be
- used either to save on SQL calls or to avoid issues with related tables
- that can't be accessed. Note that if a related table is already present
- in the :class:`_schema.MetaData` collection, or becomes present later,
- a
- :class:`_schema.ForeignKey` object associated with this
- :class:`_schema.Table` will
- resolve to that table normally.
-
- .. versionadded:: 1.3
-
- .. seealso::
-
- :paramref:`.MetaData.reflect.resolve_fks`
-
-
- :param info: Optional data dictionary which will be populated into the
- :attr:`.SchemaItem.info` attribute of this object.
-
- :param keep_existing: When ``True``, indicates that if this Table
- is already present in the given :class:`_schema.MetaData`, ignore
- further arguments within the constructor to the existing
- :class:`_schema.Table`, and return the :class:`_schema.Table`
- object as
- originally created. This is to allow a function that wishes
- to define a new :class:`_schema.Table` on first call, but on
- subsequent calls will return the same :class:`_schema.Table`,
- without any of the declarations (particularly constraints)
- being applied a second time.
-
- If :paramref:`_schema.Table.extend_existing` or
- :paramref:`_schema.Table.keep_existing` are not set,
- and the given name
- of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
- that is
- already present in the target :class:`_schema.MetaData` collection,
- and
- this :class:`_schema.Table`
- specifies additional columns or other constructs
- or flags that modify the table's state, an
- error is raised. The purpose of these two mutually-exclusive flags
- is to specify what action should be taken when a
- :class:`_schema.Table`
- is specified that matches an existing :class:`_schema.Table`,
- yet specifies
- additional constructs.
-
- .. seealso::
-
- :paramref:`_schema.Table.extend_existing`
-
- :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
- which will be passed to :func:`.event.listen` upon construction.
- This alternate hook to :func:`.event.listen` allows the establishment
- of a listener function specific to this :class:`_schema.Table` before
- the "autoload" process begins. Historically this has been intended
- for use with the :meth:`.DDLEvents.column_reflect` event, however
- note that this event hook may now be associated with the
- :class:`_schema.MetaData` object directly::
-
- def listen_for_reflect(table, column_info):
- "handle the column reflection event"
- # ...
-
- t = Table(
- 'sometable',
- autoload_with=engine,
- listeners=[
- ('column_reflect', listen_for_reflect)
- ])
-
- .. seealso::
-
- :meth:`_events.DDLEvents.column_reflect`
-
- :param must_exist: When ``True``, indicates that this Table must already
- be present in the given :class:`_schema.MetaData` collection, else
- an exception is raised.
-
- :param prefixes:
- A list of strings to insert after CREATE in the CREATE TABLE
- statement. They will be separated by spaces.
-
- :param quote: Force quoting of this table's name on or off, corresponding
- to ``True`` or ``False``. When left at its default of ``None``,
- the column identifier will be quoted according to whether the name is
- case sensitive (identifiers with at least one upper case character are
- treated as case sensitive), or if it's a reserved word. This flag
- is only needed to force quoting of a reserved word which is not known
- by the SQLAlchemy dialect.
-
- .. note:: setting this flag to ``False`` will not provide
- case-insensitive behavior for table reflection; table reflection
- will always search for a mixed-case name in a case sensitive
- fashion. Case insensitive names are specified in SQLAlchemy only
- by stating the name with all lower case characters.
-
- :param quote_schema: same as 'quote' but applies to the schema identifier.
-
- :param schema: The schema name for this table, which is required if
- the table resides in a schema other than the default selected schema
- for the engine's database connection. Defaults to ``None``.
-
- If the owning :class:`_schema.MetaData` of this :class:`_schema.Table`
- specifies its
- own :paramref:`_schema.MetaData.schema` parameter,
- then that schema name will
- be applied to this :class:`_schema.Table`
- if the schema parameter here is set
- to ``None``. To set a blank schema name on a :class:`_schema.Table`
- that
- would otherwise use the schema set on the owning
- :class:`_schema.MetaData`,
- specify the special symbol :attr:`.BLANK_SCHEMA`.
-
- .. versionadded:: 1.0.14 Added the :attr:`.BLANK_SCHEMA` symbol to
- allow a :class:`_schema.Table`
- to have a blank schema name even when the
- parent :class:`_schema.MetaData` specifies
- :paramref:`_schema.MetaData.schema`.
-
- The quoting rules for the schema name are the same as those for the
- ``name`` parameter, in that quoting is applied for reserved words or
- case-sensitive names; to enable unconditional quoting for the schema
- name, specify the flag ``quote_schema=True`` to the constructor, or use
- the :class:`.quoted_name` construct to specify the name.
-
- :param comment: Optional string that will render an SQL comment on table
- creation.
-
- .. versionadded:: 1.2 Added the :paramref:`_schema.Table.comment`
- parameter
- to :class:`_schema.Table`.
-
- :param \**kw: Additional keyword arguments not mentioned above are
- dialect specific, and passed in the form ``<dialectname>_<argname>``.
- See the documentation regarding an individual dialect at
- :ref:`dialect_toplevel` for detail on documented arguments.
-
"""
__visit_name__ = "table"
@@ -547,13 +247,21 @@ class Table(DialectKWArgs, SchemaItem, TableClause):
else:
return (self,)
- @util.deprecated_params(
- mustexist=(
- "1.4",
- "Deprecated alias of :paramref:`_schema.Table.must_exist`",
- ),
- )
- def __new__(cls, *args, **kw):
+ if not typing.TYPE_CHECKING:
+ # typing tools seem to be inconsistent in how they handle
+ # __new__, so suggest this pattern for classes that use
+ # __new__. apply typing to the __init__ method normally
+ @util.deprecated_params(
+ mustexist=(
+ "1.4",
+ "Deprecated alias of :paramref:`_schema.Table.must_exist`",
+ ),
+ )
+ def __new__(cls, *args: Any, **kw: Any) -> Any:
+ return cls._new(*args, **kw)
+
+ @classmethod
+ def _new(cls, *args, **kw):
if not args and not kw:
# python3k pickle seems to call this
return object.__new__(cls)
@@ -607,14 +315,323 @@ class Table(DialectKWArgs, SchemaItem, TableClause):
with util.safe_reraise():
metadata._remove_table(name, schema)
- def __init__(self, *args, **kw):
- """Constructor for :class:`_schema.Table`.
+ def __init__(
+ self,
+ name: str,
+ metadata: "MetaData",
+ *args: SchemaItem,
+ **kw: Any,
+ ):
+ r"""Constructor for :class:`_schema.Table`.
- This method is a no-op. See the top-level
- documentation for :class:`_schema.Table`
- for constructor arguments.
- """
+ :param name: The name of this table as represented in the database.
+
+ The table name, along with the value of the ``schema`` parameter,
+ forms a key which uniquely identifies this :class:`_schema.Table`
+ within
+ the owning :class:`_schema.MetaData` collection.
+ Additional calls to :class:`_schema.Table` with the same name,
+ metadata,
+ and schema name will return the same :class:`_schema.Table` object.
+
+ Names which contain no upper case characters
+ will be treated as case insensitive names, and will not be quoted
+ unless they are a reserved word or contain special characters.
+ A name with any number of upper case characters is considered
+ to be case sensitive, and will be sent as quoted.
+
+ To enable unconditional quoting for the table name, specify the flag
+ ``quote=True`` to the constructor, or use the :class:`.quoted_name`
+ construct to specify the name.
+
+ :param metadata: a :class:`_schema.MetaData`
+ object which will contain this
+ table. The metadata is used as a point of association of this table
+ with other tables which are referenced via foreign key. It also
+ may be used to associate this table with a particular
+ :class:`.Connection` or :class:`.Engine`.
+
+ :param \*args: Additional positional arguments are used primarily
+ to add the list of :class:`_schema.Column`
+ objects contained within this
+ table. Similar to the style of a CREATE TABLE statement, other
+ :class:`.SchemaItem` constructs may be added here, including
+ :class:`.PrimaryKeyConstraint`, and
+ :class:`_schema.ForeignKeyConstraint`.
+
+ :param autoload: Defaults to ``False``, unless
+ :paramref:`_schema.Table.autoload_with`
+ is set in which case it defaults to ``True``;
+ :class:`_schema.Column` objects
+ for this table should be reflected from the database, possibly
+ augmenting objects that were explicitly specified.
+ :class:`_schema.Column` and other objects explicitly set on the
+ table will replace corresponding reflected objects.
+
+ .. deprecated:: 1.4
+
+ The autoload parameter is deprecated and will be removed in
+ version 2.0. Please use the
+ :paramref:`_schema.Table.autoload_with` parameter, passing an
+ engine or connection.
+
+ .. seealso::
+
+ :ref:`metadata_reflection_toplevel`
+
+ :param autoload_replace: Defaults to ``True``; when using
+ :paramref:`_schema.Table.autoload`
+ in conjunction with :paramref:`_schema.Table.extend_existing`,
+ indicates
+ that :class:`_schema.Column` objects present in the already-existing
+ :class:`_schema.Table`
+ object should be replaced with columns of the same
+ name retrieved from the autoload process. When ``False``, columns
+ already present under existing names will be omitted from the
+ reflection process.
+
+ Note that this setting does not impact :class:`_schema.Column` objects
+ specified programmatically within the call to :class:`_schema.Table`
+ that
+ also is autoloading; those :class:`_schema.Column` objects will always
+ replace existing columns of the same name when
+ :paramref:`_schema.Table.extend_existing` is ``True``.
+
+ .. seealso::
+
+ :paramref:`_schema.Table.autoload`
+
+ :paramref:`_schema.Table.extend_existing`
+
+ :param autoload_with: An :class:`_engine.Engine` or
+ :class:`_engine.Connection` object,
+ or a :class:`_reflection.Inspector` object as returned by
+ :func:`_sa.inspect`
+ against one, with which this :class:`_schema.Table`
+ object will be reflected.
+ When set to a non-None value, the autoload process will take place
+ for this table against the given engine or connection.
+
+ :param extend_existing: When ``True``, indicates that if this
+ :class:`_schema.Table` is already present in the given
+ :class:`_schema.MetaData`,
+ apply further arguments within the constructor to the existing
+ :class:`_schema.Table`.
+
+ If :paramref:`_schema.Table.extend_existing` or
+ :paramref:`_schema.Table.keep_existing` are not set,
+ and the given name
+ of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
+ that is
+ already present in the target :class:`_schema.MetaData` collection,
+ and
+ this :class:`_schema.Table`
+ specifies additional columns or other constructs
+ or flags that modify the table's state, an
+ error is raised. The purpose of these two mutually-exclusive flags
+ is to specify what action should be taken when a
+ :class:`_schema.Table`
+ is specified that matches an existing :class:`_schema.Table`,
+ yet specifies
+ additional constructs.
+
+ :paramref:`_schema.Table.extend_existing`
+ will also work in conjunction
+ with :paramref:`_schema.Table.autoload` to run a new reflection
+ operation against the database, even if a :class:`_schema.Table`
+ of the same name is already present in the target
+ :class:`_schema.MetaData`; newly reflected :class:`_schema.Column`
+ objects
+ and other options will be added into the state of the
+ :class:`_schema.Table`, potentially overwriting existing columns
+ and options of the same name.
+
+ As is always the case with :paramref:`_schema.Table.autoload`,
+ :class:`_schema.Column` objects can be specified in the same
+ :class:`_schema.Table`
+ constructor, which will take precedence. Below, the existing
+ table ``mytable`` will be augmented with :class:`_schema.Column`
+ objects
+ both reflected from the database, as well as the given
+ :class:`_schema.Column`
+ named "y"::
+
+ Table("mytable", metadata,
+ Column('y', Integer),
+ extend_existing=True,
+ autoload_with=engine
+ )
+
+ .. seealso::
+
+ :paramref:`_schema.Table.autoload`
+
+ :paramref:`_schema.Table.autoload_replace`
+
+ :paramref:`_schema.Table.keep_existing`
+
+
+ :param implicit_returning: True by default - indicates that
+ RETURNING can be used by default to fetch newly inserted primary key
+ values, for backends which support this. Note that
+ :func:`_sa.create_engine` also provides an ``implicit_returning``
+ flag.
+
+ :param include_columns: A list of strings indicating a subset of
+ columns to be loaded via the ``autoload`` operation; table columns who
+ aren't present in this list will not be represented on the resulting
+ ``Table`` object. Defaults to ``None`` which indicates all columns
+ should be reflected.
+
+ :param resolve_fks: Whether or not to reflect :class:`_schema.Table`
+ objects
+ related to this one via :class:`_schema.ForeignKey` objects, when
+ :paramref:`_schema.Table.autoload` or
+ :paramref:`_schema.Table.autoload_with` is
+ specified. Defaults to True. Set to False to disable reflection of
+ related tables as :class:`_schema.ForeignKey`
+ objects are encountered; may be
+ used either to save on SQL calls or to avoid issues with related tables
+ that can't be accessed. Note that if a related table is already present
+ in the :class:`_schema.MetaData` collection, or becomes present later,
+ a
+ :class:`_schema.ForeignKey` object associated with this
+ :class:`_schema.Table` will
+ resolve to that table normally.
+
+ .. versionadded:: 1.3
+
+ .. seealso::
+
+ :paramref:`.MetaData.reflect.resolve_fks`
+
+
+ :param info: Optional data dictionary which will be populated into the
+ :attr:`.SchemaItem.info` attribute of this object.
+
+ :param keep_existing: When ``True``, indicates that if this Table
+ is already present in the given :class:`_schema.MetaData`, ignore
+ further arguments within the constructor to the existing
+ :class:`_schema.Table`, and return the :class:`_schema.Table`
+ object as
+ originally created. This is to allow a function that wishes
+ to define a new :class:`_schema.Table` on first call, but on
+ subsequent calls will return the same :class:`_schema.Table`,
+ without any of the declarations (particularly constraints)
+ being applied a second time.
+
+ If :paramref:`_schema.Table.extend_existing` or
+ :paramref:`_schema.Table.keep_existing` are not set,
+ and the given name
+ of the new :class:`_schema.Table` refers to a :class:`_schema.Table`
+ that is
+ already present in the target :class:`_schema.MetaData` collection,
+ and
+ this :class:`_schema.Table`
+ specifies additional columns or other constructs
+ or flags that modify the table's state, an
+ error is raised. The purpose of these two mutually-exclusive flags
+ is to specify what action should be taken when a
+ :class:`_schema.Table`
+ is specified that matches an existing :class:`_schema.Table`,
+ yet specifies
+ additional constructs.
+
+ .. seealso::
+
+ :paramref:`_schema.Table.extend_existing`
+
+ :param listeners: A list of tuples of the form ``(<eventname>, <fn>)``
+ which will be passed to :func:`.event.listen` upon construction.
+ This alternate hook to :func:`.event.listen` allows the establishment
+ of a listener function specific to this :class:`_schema.Table` before
+ the "autoload" process begins. Historically this has been intended
+ for use with the :meth:`.DDLEvents.column_reflect` event, however
+ note that this event hook may now be associated with the
+ :class:`_schema.MetaData` object directly::
+
+ def listen_for_reflect(table, column_info):
+ "handle the column reflection event"
+ # ...
+
+ t = Table(
+ 'sometable',
+ autoload_with=engine,
+ listeners=[
+ ('column_reflect', listen_for_reflect)
+ ])
+
+ .. seealso::
+
+ :meth:`_events.DDLEvents.column_reflect`
+
+ :param must_exist: When ``True``, indicates that this Table must already
+ be present in the given :class:`_schema.MetaData` collection, else
+ an exception is raised.
+
+ :param prefixes:
+ A list of strings to insert after CREATE in the CREATE TABLE
+ statement. They will be separated by spaces.
+
+ :param quote: Force quoting of this table's name on or off, corresponding
+ to ``True`` or ``False``. When left at its default of ``None``,
+ the column identifier will be quoted according to whether the name is
+ case sensitive (identifiers with at least one upper case character are
+ treated as case sensitive), or if it's a reserved word. This flag
+ is only needed to force quoting of a reserved word which is not known
+ by the SQLAlchemy dialect.
+
+ .. note:: setting this flag to ``False`` will not provide
+ case-insensitive behavior for table reflection; table reflection
+ will always search for a mixed-case name in a case sensitive
+ fashion. Case insensitive names are specified in SQLAlchemy only
+ by stating the name with all lower case characters.
+
+ :param quote_schema: same as 'quote' but applies to the schema identifier.
+
+ :param schema: The schema name for this table, which is required if
+ the table resides in a schema other than the default selected schema
+ for the engine's database connection. Defaults to ``None``.
+
+ If the owning :class:`_schema.MetaData` of this :class:`_schema.Table`
+ specifies its
+ own :paramref:`_schema.MetaData.schema` parameter,
+ then that schema name will
+ be applied to this :class:`_schema.Table`
+ if the schema parameter here is set
+ to ``None``. To set a blank schema name on a :class:`_schema.Table`
+ that
+ would otherwise use the schema set on the owning
+ :class:`_schema.MetaData`,
+ specify the special symbol :attr:`.BLANK_SCHEMA`.
+
+ .. versionadded:: 1.0.14 Added the :attr:`.BLANK_SCHEMA` symbol to
+ allow a :class:`_schema.Table`
+ to have a blank schema name even when the
+ parent :class:`_schema.MetaData` specifies
+ :paramref:`_schema.MetaData.schema`.
+
+ The quoting rules for the schema name are the same as those for the
+ ``name`` parameter, in that quoting is applied for reserved words or
+ case-sensitive names; to enable unconditional quoting for the schema
+ name, specify the flag ``quote_schema=True`` to the constructor, or use
+ the :class:`.quoted_name` construct to specify the name.
+
+ :param comment: Optional string that will render an SQL comment on table
+ creation.
+
+ .. versionadded:: 1.2 Added the :paramref:`_schema.Table.comment`
+ parameter
+ to :class:`_schema.Table`.
+
+ :param \**kw: Additional keyword arguments not mentioned above are
+ dialect specific, and passed in the form ``<dialectname>_<argname>``.
+ See the documentation regarding an individual dialect at
+ :ref:`dialect_toplevel` for detail on documented arguments.
+
+ """ # noqa E501
+
# __init__ is overridden to prevent __new__ from
# calling the superclass constructor.
@@ -1203,7 +1220,7 @@ class Column(DialectKWArgs, SchemaItem, ColumnClause[_T]):
) -> None:
...
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args: Any, **kwargs: Any):
r"""
Construct a new ``Column`` object.
@@ -2179,18 +2196,18 @@ class ForeignKey(DialectKWArgs, SchemaItem):
def __init__(
self,
- column,
- _constraint=None,
- use_alter=False,
- name=None,
- onupdate=None,
- ondelete=None,
- deferrable=None,
- initially=None,
- link_to_name=False,
- match=None,
- info=None,
- **dialect_kw,
+ column: Union[str, Column, SQLCoreOperations],
+ _constraint: Optional["ForeignKeyConstraint"] = None,
+ use_alter: bool = False,
+ name: Optional[str] = None,
+ onupdate: Optional[str] = None,
+ ondelete: Optional[str] = None,
+ deferrable: Optional[bool] = None,
+ initially: Optional[bool] = None,
+ link_to_name: bool = False,
+ match: Optional[str] = None,
+ info: Optional[Dict[Any, Any]] = None,
+ **dialect_kw: Any,
):
r"""
Construct a column-level FOREIGN KEY.
@@ -2337,7 +2354,7 @@ class ForeignKey(DialectKWArgs, SchemaItem):
)
return self._schema_item_copy(fk)
- def _get_colspec(self, schema=None, table_name=None):
+ def _get_colspec(self, schema=None, table_name=None, _is_copy=False):
"""Return a string based 'column specification' for this
:class:`_schema.ForeignKey`.
@@ -2357,6 +2374,14 @@ class ForeignKey(DialectKWArgs, SchemaItem):
else:
return "%s.%s" % (table_name, colname)
elif self._table_column is not None:
+ if self._table_column.table is None:
+ if _is_copy:
+ raise exc.InvalidRequestError(
+ f"Can't copy ForeignKey object which refers to "
+ f"non-table bound Column {self._table_column!r}"
+ )
+ else:
+ return self._table_column.key
return "%s.%s" % (
self._table_column.table.fullname,
self._table_column.key,
@@ -3858,6 +3883,7 @@ class ForeignKeyConstraint(ColumnCollectionConstraint):
if target_table is not None
and x._table_key() == x.parent.table.key
else None,
+ _is_copy=True,
)
for x in self.elements
],
@@ -4331,10 +4357,10 @@ class MetaData(SchemaItem):
def __init__(
self,
- schema=None,
- quote_schema=None,
- naming_convention=None,
- info=None,
+ schema: Optional[str] = None,
+ quote_schema: Optional[bool] = None,
+ naming_convention: Optional[Dict[str, str]] = None,
+ info: Optional[Dict[Any, Any]] = None,
):
"""Create a new MetaData object.
@@ -4465,7 +4491,7 @@ class MetaData(SchemaItem):
self._sequences = {}
self._fk_memos = collections.defaultdict(list)
- tables = None
+ tables: Dict[str, Table]
"""A dictionary of :class:`_schema.Table`
objects keyed to their name or "table key".
@@ -4483,10 +4509,10 @@ class MetaData(SchemaItem):
"""
- def __repr__(self):
+ def __repr__(self) -> str:
return "MetaData()"
- def __contains__(self, table_or_key):
+ def __contains__(self, table_or_key: Union[str, Table]) -> bool:
if not isinstance(table_or_key, str):
table_or_key = table_or_key.key
return table_or_key in self.tables
@@ -4530,20 +4556,20 @@ class MetaData(SchemaItem):
self._schemas = state["schemas"]
self._fk_memos = state["fk_memos"]
- def clear(self):
+ def clear(self) -> None:
"""Clear all Table objects from this MetaData."""
dict.clear(self.tables)
self._schemas.clear()
self._fk_memos.clear()
- def remove(self, table):
+ def remove(self, table: Table) -> None:
"""Remove the given Table object from this MetaData."""
self._remove_table(table.name, table.schema)
@property
- def sorted_tables(self):
+ def sorted_tables(self) -> List[Table]:
"""Returns a list of :class:`_schema.Table` objects sorted in order of
foreign key dependency.
@@ -4599,14 +4625,14 @@ class MetaData(SchemaItem):
def reflect(
self,
- bind,
- schema=None,
- views=False,
- only=None,
- extend_existing=False,
- autoload_replace=True,
- resolve_fks=True,
- **dialect_kwargs,
+ bind: Union["Engine", "Connection"],
+ schema: Optional[str] = None,
+ views: bool = False,
+ only: Optional[_typing_Sequence[str]] = None,
+ extend_existing: bool = False,
+ autoload_replace: bool = True,
+ resolve_fks: bool = True,
+ **dialect_kwargs: Any,
):
r"""Load all available table definitions from the database.
@@ -4754,7 +4780,12 @@ class MetaData(SchemaItem):
except exc.UnreflectableTableError as uerr:
util.warn("Skipping table %s: %s" % (name, uerr))
- def create_all(self, bind, tables=None, checkfirst=True):
+ def create_all(
+ self,
+ bind: Union["Engine", "Connection"],
+ tables: Optional[_typing_Sequence[Table]] = None,
+ checkfirst: bool = True,
+ ):
"""Create all tables stored in this metadata.
Conditional by default, will not attempt to recreate tables already
@@ -4777,7 +4808,12 @@ class MetaData(SchemaItem):
ddl.SchemaGenerator, self, checkfirst=checkfirst, tables=tables
)
- def drop_all(self, bind, tables=None, checkfirst=True):
+ def drop_all(
+ self,
+ bind: Union["Engine", "Connection"],
+ tables: Optional[_typing_Sequence[Table]] = None,
+ checkfirst: bool = True,
+ ):
"""Drop all tables stored in this metadata.
Conditional by default, will not attempt to drop tables not present in
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index e1bbcffec..b0985f75d 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -12,14 +12,13 @@ SQL tables and derived rowsets.
"""
import collections
+from enum import Enum
import itertools
from operator import attrgetter
import typing
from typing import Any as TODO_Any
from typing import Optional
from typing import Tuple
-from typing import Type
-from typing import Union
from . import cache_key
from . import coercions
@@ -28,6 +27,7 @@ from . import roles
from . import traversals
from . import type_api
from . import visitors
+from ._typing import _ColumnsClauseElement
from .annotation import Annotated
from .annotation import SupportsCloneAnnotations
from .base import _clone
@@ -847,8 +847,11 @@ class FromClause(roles.AnonymizedFromClauseRole, Selectable):
return self.alias(name=name)
-LABEL_STYLE_NONE = util.symbol(
- "LABEL_STYLE_NONE",
+class SelectLabelStyle(Enum):
+ """Label style constants that may be passed to
+ :meth:`_sql.Select.set_label_style`."""
+
+ LABEL_STYLE_NONE = 0
"""Label style indicating no automatic labeling should be applied to the
columns clause of a SELECT statement.
@@ -867,11 +870,9 @@ LABEL_STYLE_NONE = util.symbol(
.. versionadded:: 1.4
-""", # noqa E501
-)
+ """ # noqa E501
-LABEL_STYLE_TABLENAME_PLUS_COL = util.symbol(
- "LABEL_STYLE_TABLENAME_PLUS_COL",
+ LABEL_STYLE_TABLENAME_PLUS_COL = 1
"""Label style indicating all columns should be labeled as
``<tablename>_<columnname>`` when generating the columns clause of a SELECT
statement, to disambiguate same-named columns referenced from different
@@ -897,12 +898,9 @@ LABEL_STYLE_TABLENAME_PLUS_COL = util.symbol(
.. versionadded:: 1.4
-""", # noqa E501
-)
+ """ # noqa: E501
-
-LABEL_STYLE_DISAMBIGUATE_ONLY = util.symbol(
- "LABEL_STYLE_DISAMBIGUATE_ONLY",
+ LABEL_STYLE_DISAMBIGUATE_ONLY = 2
"""Label style indicating that columns with a name that conflicts with
an existing name should be labeled with a semi-anonymizing label
when generating the columns clause of a SELECT statement.
@@ -924,17 +922,24 @@ LABEL_STYLE_DISAMBIGUATE_ONLY = util.symbol(
.. versionadded:: 1.4
-""", # noqa: E501,
-)
+ """ # noqa: E501
+ LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY
+ """The default label style, refers to
+ :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`.
-LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY
-"""The default label style, refers to
-:data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`.
+ .. versionadded:: 1.4
-.. versionadded:: 1.4
+ """
-"""
+
+(
+ LABEL_STYLE_NONE,
+ LABEL_STYLE_TABLENAME_PLUS_COL,
+ LABEL_STYLE_DISAMBIGUATE_ONLY,
+) = list(SelectLabelStyle)
+
+LABEL_STYLE_DEFAULT = LABEL_STYLE_DISAMBIGUATE_ONLY
class Join(roles.DMLTableRole, FromClause):
@@ -2870,10 +2875,12 @@ class SelectStatementGrouping(GroupedElement, SelectBase):
else:
return self
- def get_label_style(self):
+ def get_label_style(self) -> SelectLabelStyle:
return self._label_style
- def set_label_style(self, label_style):
+ def set_label_style(
+ self, label_style: SelectLabelStyle
+ ) -> "SelectStatementGrouping":
return SelectStatementGrouping(
self.element.set_label_style(label_style)
)
@@ -3018,7 +3025,7 @@ class GenerativeSelect(SelectBase):
)
return self
- def get_label_style(self):
+ def get_label_style(self) -> SelectLabelStyle:
"""
Retrieve the current label style.
@@ -3027,14 +3034,16 @@ class GenerativeSelect(SelectBase):
"""
return self._label_style
- def set_label_style(self, style):
+ def set_label_style(
+ self: SelfGenerativeSelect, style: SelectLabelStyle
+ ) -> SelfGenerativeSelect:
"""Return a new selectable with the specified label style.
There are three "label styles" available,
- :data:`_sql.LABEL_STYLE_DISAMBIGUATE_ONLY`,
- :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL`, and
- :data:`_sql.LABEL_STYLE_NONE`. The default style is
- :data:`_sql.LABEL_STYLE_TABLENAME_PLUS_COL`.
+ :attr:`_sql.SelectLabelStyle.LABEL_STYLE_DISAMBIGUATE_ONLY`,
+ :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`, and
+ :attr:`_sql.SelectLabelStyle.LABEL_STYLE_NONE`. The default style is
+ :attr:`_sql.SelectLabelStyle.LABEL_STYLE_TABLENAME_PLUS_COL`.
In modern SQLAlchemy, there is not generally a need to change the
labeling style, as per-expression labels are more effectively used by
@@ -4131,7 +4140,7 @@ class Select(
stmt.__dict__.update(kw)
return stmt
- def __init__(self, *entities: Union[roles.ColumnsClauseRole, Type]):
+ def __init__(self, *entities: _ColumnsClauseElement):
r"""Construct a new :class:`_expression.Select`.
The public constructor for :class:`_expression.Select` is the
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
index dd29b2c3a..6b878dc70 100644
--- a/lib/sqlalchemy/sql/type_api.py
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -13,6 +13,7 @@ import typing
from typing import Any
from typing import Callable
from typing import Generic
+from typing import Optional
from typing import Tuple
from typing import Type
from typing import TypeVar
@@ -21,7 +22,7 @@ from typing import Union
from .base import SchemaEventTarget
from .cache_key import NO_CACHE
from .operators import ColumnOperators
-from .visitors import Traversible
+from .visitors import Visitable
from .. import exc
from .. import util
@@ -52,7 +53,7 @@ _CT = TypeVar("_CT", bound=Any)
SelfTypeEngine = typing.TypeVar("SelfTypeEngine", bound="TypeEngine")
-class TypeEngine(Traversible, Generic[_T]):
+class TypeEngine(Visitable, Generic[_T]):
"""The ultimate base class for all SQL datatypes.
Common subclasses of :class:`.TypeEngine` include
@@ -573,7 +574,7 @@ class TypeEngine(Traversible, Generic[_T]):
raise NotImplementedError()
def with_variant(
- self: SelfTypeEngine, type_: "TypeEngine", dialect_name: str
+ self: SelfTypeEngine, type_: "TypeEngine", *dialect_names: str
) -> SelfTypeEngine:
r"""Produce a copy of this type object that will utilize the given
type when applied to the dialect of the given name.
@@ -586,7 +587,7 @@ class TypeEngine(Traversible, Generic[_T]):
string_type = String()
string_type = string_type.with_variant(
- mysql.VARCHAR(collation='foo'), 'mysql'
+ mysql.VARCHAR(collation='foo'), 'mysql', 'mariadb'
)
The variant mapping indicates that when this type is
@@ -602,16 +603,20 @@ class TypeEngine(Traversible, Generic[_T]):
:param type\_: a :class:`.TypeEngine` that will be selected
as a variant from the originating type, when a dialect
of the given name is in use.
- :param dialect_name: base name of the dialect which uses
- this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
+ :param \*dialect_names: one or more base names of the dialect which
+ uses this type. (i.e. ``'postgresql'``, ``'mysql'``, etc.)
+
+ .. versionchanged:: 2.0 multiple dialect names can be specified
+ for one variant.
"""
- if dialect_name in self._variant_mapping:
- raise exc.ArgumentError(
- "Dialect '%s' is already present in "
- "the mapping for this %r" % (dialect_name, self)
- )
+ for dialect_name in dialect_names:
+ if dialect_name in self._variant_mapping:
+ raise exc.ArgumentError(
+ "Dialect '%s' is already present in "
+ "the mapping for this %r" % (dialect_name, self)
+ )
new_type = self.copy()
if isinstance(type_, type):
type_ = type_()
@@ -620,8 +625,9 @@ class TypeEngine(Traversible, Generic[_T]):
"can't pass a type that already has variants as a "
"dialect-level type to with_variant()"
)
+
new_type._variant_mapping = self._variant_mapping.union(
- {dialect_name: type_}
+ {dialect_name: type_ for dialect_name in dialect_names}
)
return new_type
@@ -919,7 +925,7 @@ class ExternalType:
"""
- cache_ok = None
+ cache_ok: Optional[bool] = None
"""Indicate if statements using this :class:`.ExternalType` are "safe to
cache".
@@ -1357,6 +1363,8 @@ class TypeDecorator(ExternalType, SchemaEventTarget, TypeEngine[_T]):
_is_type_decorator = True
+ impl: Union[TypeEngine[Any], Type[TypeEngine[Any]]]
+
def __init__(self, *args, **kwargs):
"""Construct a :class:`.TypeDecorator`.