first add files

This commit is contained in:
2023-10-08 20:59:00 +08:00
parent b494be364b
commit 1dac226337
991 changed files with 368151 additions and 40 deletions

View File

@@ -0,0 +1,150 @@
# sql/__init__.py
# Copyright (C) 2005-2022 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
from .base import Executable
from .compiler import COLLECT_CARTESIAN_PRODUCTS
from .compiler import FROM_LINTING
from .compiler import NO_LINTING
from .compiler import WARN_LINTING
from .expression import Alias
from .expression import alias
from .expression import all_
from .expression import and_
from .expression import any_
from .expression import asc
from .expression import between
from .expression import bindparam
from .expression import case
from .expression import cast
from .expression import ClauseElement
from .expression import collate
from .expression import column
from .expression import ColumnCollection
from .expression import ColumnElement
from .expression import CompoundSelect
from .expression import cte
from .expression import Delete
from .expression import delete
from .expression import desc
from .expression import distinct
from .expression import except_
from .expression import except_all
from .expression import exists
from .expression import extract
from .expression import false
from .expression import False_
from .expression import FromClause
from .expression import func
from .expression import funcfilter
from .expression import Insert
from .expression import insert
from .expression import intersect
from .expression import intersect_all
from .expression import Join
from .expression import join
from .expression import label
from .expression import LABEL_STYLE_DEFAULT
from .expression import LABEL_STYLE_DISAMBIGUATE_ONLY
from .expression import LABEL_STYLE_NONE
from .expression import LABEL_STYLE_TABLENAME_PLUS_COL
from .expression import lambda_stmt
from .expression import LambdaElement
from .expression import lateral
from .expression import literal
from .expression import literal_column
from .expression import modifier
from .expression import not_
from .expression import null
from .expression import nulls_first
from .expression import nulls_last
from .expression import nullsfirst
from .expression import nullslast
from .expression import or_
from .expression import outerjoin
from .expression import outparam
from .expression import over
from .expression import quoted_name
from .expression import Select
from .expression import select
from .expression import Selectable
from .expression import StatementLambdaElement
from .expression import Subquery
from .expression import subquery
from .expression import table
from .expression import TableClause
from .expression import TableSample
from .expression import tablesample
from .expression import text
from .expression import true
from .expression import True_
from .expression import tuple_
from .expression import type_coerce
from .expression import union
from .expression import union_all
from .expression import Update
from .expression import update
from .expression import Values
from .expression import values
from .expression import within_group
from .visitors import ClauseVisitor
def __go(lcls):
global __all__
from .. import util as _sa_util
import inspect as _inspect
__all__ = sorted(
name
for name, obj in lcls.items()
if not (name.startswith("_") or _inspect.ismodule(obj))
)
from .annotation import _prepare_annotations
from .annotation import Annotated
from .elements import AnnotatedColumnElement
from .elements import ClauseList
from .selectable import AnnotatedFromClause
# from .traversals import _preconfigure_traversals
from . import base
from . import coercions
from . import elements
from . import events
from . import lambdas
from . import selectable
from . import schema
from . import sqltypes
from . import traversals
from . import type_api
base.coercions = elements.coercions = coercions
base.elements = elements
base.type_api = type_api
coercions.elements = elements
coercions.lambdas = lambdas
coercions.schema = schema
coercions.selectable = selectable
coercions.sqltypes = sqltypes
coercions.traversals = traversals
_prepare_annotations(ColumnElement, AnnotatedColumnElement)
_prepare_annotations(FromClause, AnnotatedFromClause)
_prepare_annotations(ClauseList, Annotated)
# this is expensive at import time; elements that are used can create
# their traversals on demand
# _preconfigure_traversals(ClauseElement)
_sa_util.preloaded.import_prefix("sqlalchemy.sql")
from . import naming
__go(locals())

View File

@@ -0,0 +1,364 @@
# sql/annotation.py
# Copyright (C) 2005-2022 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
"""The :class:`.Annotated` class and related routines; creates hash-equivalent
copies of SQL constructs which contain context-specific markers and
associations.
"""
from . import operators
from .base import HasCacheKey
from .traversals import anon_map
from .visitors import InternalTraversal
from .. import util
EMPTY_ANNOTATIONS = util.immutabledict()
class SupportsAnnotations(object):
_annotations = EMPTY_ANNOTATIONS
@util.memoized_property
def _annotations_cache_key(self):
anon_map_ = anon_map()
return (
"_annotations",
tuple(
(
key,
value._gen_cache_key(anon_map_, [])
if isinstance(value, HasCacheKey)
else value,
)
for key, value in [
(key, self._annotations[key])
for key in sorted(self._annotations)
]
),
)
class SupportsCloneAnnotations(SupportsAnnotations):
_clone_annotations_traverse_internals = [
("_annotations", InternalTraversal.dp_annotations_key)
]
def _annotate(self, values):
"""return a copy of this ClauseElement with annotations
updated by the given dictionary.
"""
new = self._clone()
new._annotations = new._annotations.union(values)
new.__dict__.pop("_annotations_cache_key", None)
new.__dict__.pop("_generate_cache_key", None)
return new
def _with_annotations(self, values):
"""return a copy of this ClauseElement with annotations
replaced by the given dictionary.
"""
new = self._clone()
new._annotations = util.immutabledict(values)
new.__dict__.pop("_annotations_cache_key", None)
new.__dict__.pop("_generate_cache_key", None)
return new
def _deannotate(self, values=None, clone=False):
"""return a copy of this :class:`_expression.ClauseElement`
with annotations
removed.
:param values: optional tuple of individual values
to remove.
"""
if clone or self._annotations:
# clone is used when we are also copying
# the expression for a deep deannotation
new = self._clone()
new._annotations = util.immutabledict()
new.__dict__.pop("_annotations_cache_key", None)
return new
else:
return self
class SupportsWrappingAnnotations(SupportsAnnotations):
def _annotate(self, values):
"""return a copy of this ClauseElement with annotations
updated by the given dictionary.
"""
return Annotated(self, values)
def _with_annotations(self, values):
"""return a copy of this ClauseElement with annotations
replaced by the given dictionary.
"""
return Annotated(self, values)
def _deannotate(self, values=None, clone=False):
"""return a copy of this :class:`_expression.ClauseElement`
with annotations
removed.
:param values: optional tuple of individual values
to remove.
"""
if clone:
s = self._clone()
return s
else:
return self
class Annotated(object):
"""clones a SupportsAnnotated and applies an 'annotations' dictionary.
Unlike regular clones, this clone also mimics __hash__() and
__cmp__() of the original element so that it takes its place
in hashed collections.
A reference to the original element is maintained, for the important
reason of keeping its hash value current. When GC'ed, the
hash value may be reused, causing conflicts.
.. note:: The rationale for Annotated producing a brand new class,
rather than placing the functionality directly within ClauseElement,
is **performance**. The __hash__() method is absent on plain
ClauseElement which leads to significantly reduced function call
overhead, as the use of sets and dictionaries against ClauseElement
objects is prevalent, but most are not "annotated".
"""
_is_column_operators = False
def __new__(cls, *args):
if not args:
# clone constructor
return object.__new__(cls)
else:
element, values = args
# pull appropriate subclass from registry of annotated
# classes
try:
cls = annotated_classes[element.__class__]
except KeyError:
cls = _new_annotation_type(element.__class__, cls)
return object.__new__(cls)
def __init__(self, element, values):
self.__dict__ = element.__dict__.copy()
self.__dict__.pop("_annotations_cache_key", None)
self.__dict__.pop("_generate_cache_key", None)
self.__element = element
self._annotations = util.immutabledict(values)
self._hash = hash(element)
def _annotate(self, values):
_values = self._annotations.union(values)
return self._with_annotations(_values)
def _with_annotations(self, values):
clone = self.__class__.__new__(self.__class__)
clone.__dict__ = self.__dict__.copy()
clone.__dict__.pop("_annotations_cache_key", None)
clone.__dict__.pop("_generate_cache_key", None)
clone._annotations = values
return clone
def _deannotate(self, values=None, clone=True):
if values is None:
return self.__element
else:
return self._with_annotations(
util.immutabledict(
{
key: value
for key, value in self._annotations.items()
if key not in values
}
)
)
def _compiler_dispatch(self, visitor, **kw):
return self.__element.__class__._compiler_dispatch(self, visitor, **kw)
@property
def _constructor(self):
return self.__element._constructor
def _clone(self, **kw):
clone = self.__element._clone(**kw)
if clone is self.__element:
# detect immutable, don't change anything
return self
else:
# update the clone with any changes that have occurred
# to this object's __dict__.
clone.__dict__.update(self.__dict__)
return self.__class__(clone, self._annotations)
def __reduce__(self):
return self.__class__, (self.__element, self._annotations)
def __hash__(self):
return self._hash
def __eq__(self, other):
if self._is_column_operators:
return self.__element.__class__.__eq__(self, other)
else:
return hash(other) == hash(self)
@property
def entity_namespace(self):
if "entity_namespace" in self._annotations:
return self._annotations["entity_namespace"].entity_namespace
else:
return self.__element.entity_namespace
# hard-generate Annotated subclasses. this technique
# is used instead of on-the-fly types (i.e. type.__new__())
# so that the resulting objects are pickleable; additionally, other
# decisions can be made up front about the type of object being annotated
# just once per class rather than per-instance.
annotated_classes = {}
def _deep_annotate(
element, annotations, exclude=None, detect_subquery_cols=False
):
"""Deep copy the given ClauseElement, annotating each element
with the given annotations dictionary.
Elements within the exclude collection will be cloned but not annotated.
"""
# annotated objects hack the __hash__() method so if we want to
# uniquely process them we have to use id()
cloned_ids = {}
def clone(elem, **kw):
kw["detect_subquery_cols"] = detect_subquery_cols
id_ = id(elem)
if id_ in cloned_ids:
return cloned_ids[id_]
if (
exclude
and hasattr(elem, "proxy_set")
and elem.proxy_set.intersection(exclude)
):
newelem = elem._clone(clone=clone, **kw)
elif annotations != elem._annotations:
if detect_subquery_cols and elem._is_immutable:
newelem = elem._clone(clone=clone, **kw)._annotate(annotations)
else:
newelem = elem._annotate(annotations)
else:
newelem = elem
newelem._copy_internals(clone=clone)
cloned_ids[id_] = newelem
return newelem
if element is not None:
element = clone(element)
clone = None # remove gc cycles
return element
def _deep_deannotate(element, values=None):
"""Deep copy the given element, removing annotations."""
cloned = {}
def clone(elem, **kw):
if values:
key = id(elem)
else:
key = elem
if key not in cloned:
newelem = elem._deannotate(values=values, clone=True)
newelem._copy_internals(clone=clone)
cloned[key] = newelem
return newelem
else:
return cloned[key]
if element is not None:
element = clone(element)
clone = None # remove gc cycles
return element
def _shallow_annotate(element, annotations):
"""Annotate the given ClauseElement and copy its internals so that
internal objects refer to the new annotated object.
Basically used to apply a "don't traverse" annotation to a
selectable, without digging throughout the whole
structure wasting time.
"""
element = element._annotate(annotations)
element._copy_internals()
return element
def _new_annotation_type(cls, base_cls):
if issubclass(cls, Annotated):
return cls
elif cls in annotated_classes:
return annotated_classes[cls]
for super_ in cls.__mro__:
# check if an Annotated subclass more specific than
# the given base_cls is already registered, such
# as AnnotatedColumnElement.
if super_ in annotated_classes:
base_cls = annotated_classes[super_]
break
annotated_classes[cls] = anno_cls = type(
"Annotated%s" % cls.__name__, (base_cls, cls), {}
)
globals()["Annotated%s" % cls.__name__] = anno_cls
if "_traverse_internals" in cls.__dict__:
anno_cls._traverse_internals = list(cls._traverse_internals) + [
("_annotations", InternalTraversal.dp_annotations_key)
]
elif cls.__dict__.get("inherit_cache", False):
anno_cls._traverse_internals = list(cls._traverse_internals) + [
("_annotations", InternalTraversal.dp_annotations_key)
]
# some classes include this even if they have traverse_internals
# e.g. BindParameter, add it if present.
if cls.__dict__.get("inherit_cache", False):
anno_cls.inherit_cache = True
anno_cls._is_column_operators = issubclass(cls, operators.ColumnOperators)
return anno_cls
def _prepare_annotations(target_hierarchy, base_cls):
for cls in util.walk_subclasses(target_hierarchy):
_new_annotation_type(cls, base_cls)

1702
lib/sqlalchemy/sql/base.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1091
lib/sqlalchemy/sql/crud.py Normal file

File diff suppressed because it is too large Load Diff

1341
lib/sqlalchemy/sql/ddl.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
# sql/default_comparator.py
# Copyright (C) 2005-2022 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
"""Default implementation of SQL comparison operations.
"""
from . import coercions
from . import operators
from . import roles
from . import type_api
from .elements import and_
from .elements import BinaryExpression
from .elements import ClauseList
from .elements import collate
from .elements import CollectionAggregate
from .elements import False_
from .elements import Null
from .elements import or_
from .elements import True_
from .elements import UnaryExpression
from .. import exc
from .. import util
def _boolean_compare(
expr,
op,
obj,
negate=None,
reverse=False,
_python_is_types=(util.NoneType, bool),
_any_all_expr=False,
result_type=None,
**kwargs
):
if result_type is None:
result_type = type_api.BOOLEANTYPE
if isinstance(obj, _python_is_types + (Null, True_, False_)):
# allow x ==/!= True/False to be treated as a literal.
# this comes out to "== / != true/false" or "1/0" if those
# constants aren't supported and works on all platforms
if op in (operators.eq, operators.ne) and isinstance(
obj, (bool, True_, False_)
):
return BinaryExpression(
expr,
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
negate=negate,
modifiers=kwargs,
)
elif op in (
operators.is_distinct_from,
operators.is_not_distinct_from,
):
return BinaryExpression(
expr,
coercions.expect(roles.ConstExprRole, obj),
op,
type_=result_type,
negate=negate,
modifiers=kwargs,
)
elif _any_all_expr:
obj = coercions.expect(
roles.ConstExprRole, element=obj, operator=op, expr=expr
)
else:
# all other None uses IS, IS NOT
if op in (operators.eq, operators.is_):
return BinaryExpression(
expr,
coercions.expect(roles.ConstExprRole, obj),
operators.is_,
negate=operators.is_not,
type_=result_type,
)
elif op in (operators.ne, operators.is_not):
return BinaryExpression(
expr,
coercions.expect(roles.ConstExprRole, obj),
operators.is_not,
negate=operators.is_,
type_=result_type,
)
else:
raise exc.ArgumentError(
"Only '=', '!=', 'is_()', 'is_not()', "
"'is_distinct_from()', 'is_not_distinct_from()' "
"operators can be used with None/True/False"
)
else:
obj = coercions.expect(
roles.BinaryElementRole, element=obj, operator=op, expr=expr
)
if reverse:
return BinaryExpression(
obj, expr, op, type_=result_type, negate=negate, modifiers=kwargs
)
else:
return BinaryExpression(
expr, obj, op, type_=result_type, negate=negate, modifiers=kwargs
)
def _custom_op_operate(expr, op, obj, reverse=False, result_type=None, **kw):
if result_type is None:
if op.return_type:
result_type = op.return_type
elif op.is_comparison:
result_type = type_api.BOOLEANTYPE
return _binary_operate(
expr, op, obj, reverse=reverse, result_type=result_type, **kw
)
def _binary_operate(expr, op, obj, reverse=False, result_type=None, **kw):
obj = coercions.expect(
roles.BinaryElementRole, obj, expr=expr, operator=op
)
if reverse:
left, right = obj, expr
else:
left, right = expr, obj
if result_type is None:
op, result_type = left.comparator._adapt_expression(
op, right.comparator
)
return BinaryExpression(left, right, op, type_=result_type, modifiers=kw)
def _conjunction_operate(expr, op, other, **kw):
if op is operators.and_:
return and_(expr, other)
elif op is operators.or_:
return or_(expr, other)
else:
raise NotImplementedError()
def _scalar(expr, op, fn, **kw):
return fn(expr)
def _in_impl(expr, op, seq_or_selectable, negate_op, **kw):
seq_or_selectable = coercions.expect(
roles.InElementRole, seq_or_selectable, expr=expr, operator=op
)
if "in_ops" in seq_or_selectable._annotations:
op, negate_op = seq_or_selectable._annotations["in_ops"]
return _boolean_compare(
expr, op, seq_or_selectable, negate=negate_op, **kw
)
def _getitem_impl(expr, op, other, **kw):
if (
isinstance(expr.type, type_api.INDEXABLE)
or isinstance(expr.type, type_api.TypeDecorator)
and isinstance(expr.type.impl, type_api.INDEXABLE)
):
other = coercions.expect(
roles.BinaryElementRole, other, expr=expr, operator=op
)
return _binary_operate(expr, op, other, **kw)
else:
_unsupported_impl(expr, op, other, **kw)
def _unsupported_impl(expr, op, *arg, **kw):
raise NotImplementedError(
"Operator '%s' is not supported on " "this expression" % op.__name__
)
def _inv_impl(expr, op, **kw):
"""See :meth:`.ColumnOperators.__inv__`."""
# undocumented element currently used by the ORM for
# relationship.contains()
if hasattr(expr, "negation_clause"):
return expr.negation_clause
else:
return expr._negate()
def _neg_impl(expr, op, **kw):
"""See :meth:`.ColumnOperators.__neg__`."""
return UnaryExpression(expr, operator=operators.neg, type_=expr.type)
def _match_impl(expr, op, other, **kw):
"""See :meth:`.ColumnOperators.match`."""
return _boolean_compare(
expr,
operators.match_op,
coercions.expect(
roles.BinaryElementRole,
other,
expr=expr,
operator=operators.match_op,
),
result_type=type_api.MATCHTYPE,
negate=operators.not_match_op
if op is operators.match_op
else operators.match_op,
**kw
)
def _distinct_impl(expr, op, **kw):
"""See :meth:`.ColumnOperators.distinct`."""
return UnaryExpression(
expr, operator=operators.distinct_op, type_=expr.type
)
def _between_impl(expr, op, cleft, cright, **kw):
"""See :meth:`.ColumnOperators.between`."""
return BinaryExpression(
expr,
ClauseList(
coercions.expect(
roles.BinaryElementRole,
cleft,
expr=expr,
operator=operators.and_,
),
coercions.expect(
roles.BinaryElementRole,
cright,
expr=expr,
operator=operators.and_,
),
operator=operators.and_,
group=False,
group_contents=False,
),
op,
negate=operators.not_between_op
if op is operators.between_op
else operators.between_op,
modifiers=kw,
)
def _collate_impl(expr, op, other, **kw):
return collate(expr, other)
def _regexp_match_impl(expr, op, pattern, flags, **kw):
if flags is not None:
flags = coercions.expect(
roles.BinaryElementRole,
flags,
expr=expr,
operator=operators.regexp_replace_op,
)
return _boolean_compare(
expr,
op,
pattern,
flags=flags,
negate=operators.not_regexp_match_op
if op is operators.regexp_match_op
else operators.regexp_match_op,
**kw
)
def _regexp_replace_impl(expr, op, pattern, replacement, flags, **kw):
replacement = coercions.expect(
roles.BinaryElementRole,
replacement,
expr=expr,
operator=operators.regexp_replace_op,
)
if flags is not None:
flags = coercions.expect(
roles.BinaryElementRole,
flags,
expr=expr,
operator=operators.regexp_replace_op,
)
return _binary_operate(
expr, op, pattern, replacement=replacement, flags=flags, **kw
)
# a mapping of operators with the method they use, along with
# their negated operator for comparison operators
operator_lookup = {
"and_": (_conjunction_operate,),
"or_": (_conjunction_operate,),
"inv": (_inv_impl,),
"add": (_binary_operate,),
"mul": (_binary_operate,),
"sub": (_binary_operate,),
"div": (_binary_operate,),
"mod": (_binary_operate,),
"truediv": (_binary_operate,),
"custom_op": (_custom_op_operate,),
"json_path_getitem_op": (_binary_operate,),
"json_getitem_op": (_binary_operate,),
"concat_op": (_binary_operate,),
"any_op": (_scalar, CollectionAggregate._create_any),
"all_op": (_scalar, CollectionAggregate._create_all),
"lt": (_boolean_compare, operators.ge),
"le": (_boolean_compare, operators.gt),
"ne": (_boolean_compare, operators.eq),
"gt": (_boolean_compare, operators.le),
"ge": (_boolean_compare, operators.lt),
"eq": (_boolean_compare, operators.ne),
"is_distinct_from": (_boolean_compare, operators.is_not_distinct_from),
"is_not_distinct_from": (_boolean_compare, operators.is_distinct_from),
"like_op": (_boolean_compare, operators.not_like_op),
"ilike_op": (_boolean_compare, operators.not_ilike_op),
"not_like_op": (_boolean_compare, operators.like_op),
"not_ilike_op": (_boolean_compare, operators.ilike_op),
"contains_op": (_boolean_compare, operators.not_contains_op),
"startswith_op": (_boolean_compare, operators.not_startswith_op),
"endswith_op": (_boolean_compare, operators.not_endswith_op),
"desc_op": (_scalar, UnaryExpression._create_desc),
"asc_op": (_scalar, UnaryExpression._create_asc),
"nulls_first_op": (_scalar, UnaryExpression._create_nulls_first),
"nulls_last_op": (_scalar, UnaryExpression._create_nulls_last),
"in_op": (_in_impl, operators.not_in_op),
"not_in_op": (_in_impl, operators.in_op),
"is_": (_boolean_compare, operators.is_),
"is_not": (_boolean_compare, operators.is_not),
"collate": (_collate_impl,),
"match_op": (_match_impl,),
"not_match_op": (_match_impl,),
"distinct_op": (_distinct_impl,),
"between_op": (_between_impl,),
"not_between_op": (_between_impl,),
"neg": (_neg_impl,),
"getitem": (_getitem_impl,),
"lshift": (_unsupported_impl,),
"rshift": (_unsupported_impl,),
"contains": (_unsupported_impl,),
"regexp_match_op": (_regexp_match_impl,),
"not_regexp_match_op": (_regexp_match_impl,),
"regexp_replace_op": (_regexp_replace_impl,),
}

1514
lib/sqlalchemy/sql/dml.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
# sqlalchemy/sql/events.py
# Copyright (C) 2005-2022 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
from .base import SchemaEventTarget
from .. import event
class DDLEvents(event.Events):
"""
Define event listeners for schema objects,
that is, :class:`.SchemaItem` and other :class:`.SchemaEventTarget`
subclasses, including :class:`_schema.MetaData`, :class:`_schema.Table`,
:class:`_schema.Column`.
:class:`_schema.MetaData` and :class:`_schema.Table` support events
specifically regarding when CREATE and DROP
DDL is emitted to the database.
Attachment events are also provided to customize
behavior whenever a child schema element is associated
with a parent, such as, when a :class:`_schema.Column` is associated
with its :class:`_schema.Table`, when a
:class:`_schema.ForeignKeyConstraint`
is associated with a :class:`_schema.Table`, etc.
Example using the ``after_create`` event::
from sqlalchemy import event
from sqlalchemy import Table, Column, Metadata, Integer
m = MetaData()
some_table = Table('some_table', m, Column('data', Integer))
def after_create(target, connection, **kw):
connection.execute(text(
"ALTER TABLE %s SET name=foo_%s" % (target.name, target.name)
))
event.listen(some_table, "after_create", after_create)
DDL events integrate closely with the
:class:`.DDL` class and the :class:`.DDLElement` hierarchy
of DDL clause constructs, which are themselves appropriate
as listener callables::
from sqlalchemy import DDL
event.listen(
some_table,
"after_create",
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s")
)
The methods here define the name of an event as well
as the names of members that are passed to listener
functions.
For all :class:`.DDLEvent` events, the ``propagate=True`` keyword argument
will ensure that a given event handler is propagated to copies of the
object, which are made when using the :meth:`_schema.Table.to_metadata`
method::
from sqlalchemy import DDL
event.listen(
some_table,
"after_create",
DDL("ALTER TABLE %(table)s SET name=foo_%(table)s"),
propagate=True
)
new_table = some_table.to_metadata(new_metadata)
The above :class:`.DDL` object will also be associated with the
:class:`_schema.Table` object represented by ``new_table``.
.. seealso::
:ref:`event_toplevel`
:class:`.DDLElement`
:class:`.DDL`
:ref:`schema_ddl_sequences`
"""
_target_class_doc = "SomeSchemaClassOrObject"
_dispatch_target = SchemaEventTarget
def before_create(self, target, connection, **kw):
r"""Called before CREATE statements are emitted.
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
object which is the target of the event.
:param connection: the :class:`_engine.Connection` where the
CREATE statement or statements will be emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
:func:`.event.listen` accepts the ``insert=True``
modifier for this event; when True, the listener function will
be prepended to the internal list of events upon discovery, and execute
before registered listener functions that do not pass this argument.
"""
def after_create(self, target, connection, **kw):
r"""Called after CREATE statements are emitted.
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
object which is the target of the event.
:param connection: the :class:`_engine.Connection` where the
CREATE statement or statements have been emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def before_drop(self, target, connection, **kw):
r"""Called before DROP statements are emitted.
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
object which is the target of the event.
:param connection: the :class:`_engine.Connection` where the
DROP statement or statements will be emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def after_drop(self, target, connection, **kw):
r"""Called after DROP statements are emitted.
:param target: the :class:`_schema.MetaData` or :class:`_schema.Table`
object which is the target of the event.
:param connection: the :class:`_engine.Connection` where the
DROP statement or statements have been emitted.
:param \**kw: additional keyword arguments relevant
to the event. The contents of this dictionary
may vary across releases, and include the
list of tables being generated for a metadata-level
event, the checkfirst flag, and other
elements used by internal events.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def before_parent_attach(self, target, parent):
"""Called before a :class:`.SchemaItem` is associated with
a parent :class:`.SchemaItem`.
:param target: the target object
:param parent: the parent to which the target is being attached.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def after_parent_attach(self, target, parent):
"""Called after a :class:`.SchemaItem` is associated with
a parent :class:`.SchemaItem`.
:param target: the target object
:param parent: the parent to which the target is being attached.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
"""
def _sa_event_column_added_to_pk_constraint(self, const, col):
"""internal event hook used for primary key naming convention
updates.
"""
def column_reflect(self, inspector, table, column_info):
"""Called for each unit of 'column info' retrieved when
a :class:`_schema.Table` is being reflected.
This event is most easily used by applying it to a specific
:class:`_schema.MetaData` instance, where it will take effect for
all :class:`_schema.Table` objects within that
:class:`_schema.MetaData` that undergo reflection::
metadata = MetaData()
@event.listens_for(metadata, 'column_reflect')
def receive_column_reflect(inspector, table, column_info):
# receives for all Table objects that are reflected
# under this MetaData
# will use the above event hook
my_table = Table("my_table", metadata, autoload_with=some_engine)
.. versionadded:: 1.4.0b2 The :meth:`_events.DDLEvents.column_reflect`
hook may now be applied to a :class:`_schema.MetaData` object as
well as the :class:`_schema.MetaData` class itself where it will
take place for all :class:`_schema.Table` objects associated with
the targeted :class:`_schema.MetaData`.
It may also be applied to the :class:`_schema.Table` class across
the board::
from sqlalchemy import Table
@event.listens_for(Table, 'column_reflect')
def receive_column_reflect(inspector, table, column_info):
# receives for all Table objects that are reflected
It can also be applied to a specific :class:`_schema.Table` at the
point that one is being reflected using the
:paramref:`_schema.Table.listeners` parameter::
t1 = Table(
"my_table",
autoload_with=some_engine,
listeners=[
('column_reflect', receive_column_reflect)
]
)
A future release will allow it to be associated with a specific
:class:`_schema.MetaData` object as well.
The dictionary of column information as returned by the
dialect is passed, and can be modified. The dictionary
is that returned in each element of the list returned
by :meth:`.reflection.Inspector.get_columns`:
* ``name`` - the column's name, is applied to the
:paramref:`_schema.Column.name` parameter
* ``type`` - the type of this column, which should be an instance
of :class:`~sqlalchemy.types.TypeEngine`, is applied to the
:paramref:`_schema.Column.type` parameter
* ``nullable`` - boolean flag if the column is NULL or NOT NULL,
is applied to the :paramref:`_schema.Column.nullable` parameter
* ``default`` - the column's server default value. This is
normally specified as a plain string SQL expression, however the
event can pass a :class:`.FetchedValue`, :class:`.DefaultClause`,
or :func:`_expression.text` object as well. Is applied to the
:paramref:`_schema.Column.server_default` parameter
The event is called before any action is taken against
this dictionary, and the contents can be modified; the following
additional keys may be added to the dictionary to further modify
how the :class:`_schema.Column` is constructed:
* ``key`` - the string key that will be used to access this
:class:`_schema.Column` in the ``.c`` collection; will be applied
to the :paramref:`_schema.Column.key` parameter. Is also used
for ORM mapping. See the section
:ref:`mapper_automated_reflection_schemes` for an example.
* ``quote`` - force or un-force quoting on the column name;
is applied to the :paramref:`_schema.Column.quote` parameter.
* ``info`` - a dictionary of arbitrary data to follow along with
the :class:`_schema.Column`, is applied to the
:paramref:`_schema.Column.info` parameter.
:func:`.event.listen` also accepts the ``propagate=True``
modifier for this event; when True, the listener function will
be established for any copies made of the target object,
i.e. those copies that are generated when
:meth:`_schema.Table.to_metadata` is used.
.. seealso::
:ref:`mapper_automated_reflection_schemes` -
in the ORM mapping documentation
:ref:`automap_intercepting_columns` -
in the :ref:`automap_toplevel` documentation
:ref:`metadata_reflection_dbagnostic_types` - in
the :ref:`metadata_reflection_toplevel` documentation
"""

View File

@@ -0,0 +1,278 @@
# sql/expression.py
# Copyright (C) 2005-2022 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
"""Defines the public namespace for SQL expression constructs.
Prior to version 0.9, this module contained all of "elements", "dml",
"default_comparator" and "selectable". The module was broken up
and most "factory" functions were moved to be grouped with their associated
class.
"""
__all__ = [
"Alias",
"AliasedReturnsRows",
"any_",
"all_",
"CacheKey",
"ClauseElement",
"ColumnCollection",
"ColumnElement",
"CompoundSelect",
"Delete",
"FromClause",
"Insert",
"Join",
"Lateral",
"LambdaElement",
"StatementLambdaElement",
"Select",
"Selectable",
"TableClause",
"TableValuedAlias",
"Update",
"Values",
"alias",
"and_",
"asc",
"between",
"bindparam",
"case",
"cast",
"column",
"custom_op",
"cte",
"delete",
"desc",
"distinct",
"except_",
"except_all",
"exists",
"extract",
"func",
"modifier",
"collate",
"insert",
"intersect",
"intersect_all",
"join",
"label",
"lateral",
"lambda_stmt",
"literal",
"literal_column",
"not_",
"null",
"nulls_first",
"nulls_last",
"or_",
"outparam",
"outerjoin",
"over",
"select",
"table",
"text",
"tuple_",
"type_coerce",
"quoted_name",
"union",
"union_all",
"update",
"quoted_name",
"within_group",
"Subquery",
"TableSample",
"tablesample",
"values",
]
from .base import _from_objects
from .base import _select_iterables
from .base import ColumnCollection
from .base import Executable
from .base import PARSE_AUTOCOMMIT
from .dml import Delete
from .dml import Insert
from .dml import Update
from .dml import UpdateBase
from .dml import ValuesBase
from .elements import _truncated_label
from .elements import between
from .elements import BinaryExpression
from .elements import BindParameter
from .elements import BooleanClauseList
from .elements import Case
from .elements import Cast
from .elements import ClauseElement
from .elements import ClauseList
from .elements import collate
from .elements import CollectionAggregate
from .elements import ColumnClause
from .elements import ColumnElement
from .elements import Extract
from .elements import False_
from .elements import FunctionFilter
from .elements import Grouping
from .elements import Label
from .elements import literal
from .elements import literal_column
from .elements import not_
from .elements import Null
from .elements import outparam
from .elements import Over
from .elements import quoted_name
from .elements import ReleaseSavepointClause
from .elements import RollbackToSavepointClause
from .elements import SavepointClause
from .elements import TextClause
from .elements import True_
from .elements import Tuple
from .elements import TypeClause
from .elements import TypeCoerce
from .elements import UnaryExpression
from .elements import WithinGroup
from .functions import func
from .functions import Function
from .functions import FunctionElement
from .functions import modifier
from .lambdas import lambda_stmt
from .lambdas import LambdaElement
from .lambdas import StatementLambdaElement
from .operators import ColumnOperators
from .operators import custom_op
from .operators import Operators
from .selectable import Alias
from .selectable import AliasedReturnsRows
from .selectable import CompoundSelect
from .selectable import CTE
from .selectable import Exists
from .selectable import FromClause
from .selectable import FromGrouping
from .selectable import GenerativeSelect
from .selectable import HasCTE
from .selectable import HasPrefixes
from .selectable import HasSuffixes
from .selectable import Join
from .selectable import LABEL_STYLE_DEFAULT
from .selectable import LABEL_STYLE_DISAMBIGUATE_ONLY
from .selectable import LABEL_STYLE_NONE
from .selectable import LABEL_STYLE_TABLENAME_PLUS_COL
from .selectable import Lateral
from .selectable import ReturnsRows
from .selectable import ScalarSelect
from .selectable import Select
from .selectable import Selectable
from .selectable import SelectBase
from .selectable import Subquery
from .selectable import subquery
from .selectable import TableClause
from .selectable import TableSample
from .selectable import TableValuedAlias
from .selectable import TextAsFrom
from .selectable import TextualSelect
from .selectable import Values
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.
all_ = public_factory(CollectionAggregate._create_all, ".sql.expression.all_")
any_ = public_factory(CollectionAggregate._create_any, ".sql.expression.any_")
and_ = public_factory(BooleanClauseList.and_, ".sql.expression.and_")
alias = public_factory(Alias._factory, ".sql.expression.alias")
tablesample = public_factory(
TableSample._factory, ".sql.expression.tablesample"
)
lateral = public_factory(Lateral._factory, ".sql.expression.lateral")
or_ = public_factory(BooleanClauseList.or_, ".sql.expression.or_")
bindparam = public_factory(BindParameter, ".sql.expression.bindparam")
select = public_factory(Select._create, ".sql.expression.select")
text = public_factory(TextClause._create_text, ".sql.expression.text")
table = public_factory(TableClause, ".sql.expression.table")
column = public_factory(ColumnClause, ".sql.expression.column")
over = public_factory(Over, ".sql.expression.over")
within_group = public_factory(WithinGroup, ".sql.expression.within_group")
label = public_factory(Label, ".sql.expression.label")
case = public_factory(Case, ".sql.expression.case")
cast = public_factory(Cast, ".sql.expression.cast")
cte = public_factory(CTE._factory, ".sql.expression.cte")
values = public_factory(Values, ".sql.expression.values")
extract = public_factory(Extract, ".sql.expression.extract")
tuple_ = public_factory(Tuple, ".sql.expression.tuple_")
except_ = public_factory(
CompoundSelect._create_except, ".sql.expression.except_"
)
except_all = public_factory(
CompoundSelect._create_except_all, ".sql.expression.except_all"
)
intersect = public_factory(
CompoundSelect._create_intersect, ".sql.expression.intersect"
)
intersect_all = public_factory(
CompoundSelect._create_intersect_all, ".sql.expression.intersect_all"
)
union = public_factory(CompoundSelect._create_union, ".sql.expression.union")
union_all = public_factory(
CompoundSelect._create_union_all, ".sql.expression.union_all"
)
exists = public_factory(Exists, ".sql.expression.exists")
nulls_first = public_factory(
UnaryExpression._create_nulls_first, ".sql.expression.nulls_first"
)
nullsfirst = nulls_first # deprecated 1.4; see #5435
nulls_last = public_factory(
UnaryExpression._create_nulls_last, ".sql.expression.nulls_last"
)
nullslast = nulls_last # deprecated 1.4; see #5435
asc = public_factory(UnaryExpression._create_asc, ".sql.expression.asc")
desc = public_factory(UnaryExpression._create_desc, ".sql.expression.desc")
distinct = public_factory(
UnaryExpression._create_distinct, ".sql.expression.distinct"
)
type_coerce = public_factory(TypeCoerce, ".sql.expression.type_coerce")
true = public_factory(True_._instance, ".sql.expression.true")
false = public_factory(False_._instance, ".sql.expression.false")
null = public_factory(Null._instance, ".sql.expression.null")
join = public_factory(Join._create_join, ".sql.expression.join")
outerjoin = public_factory(Join._create_outerjoin, ".sql.expression.outerjoin")
insert = public_factory(Insert, ".sql.expression.insert")
update = public_factory(Update, ".sql.expression.update")
delete = public_factory(Delete, ".sql.expression.delete")
funcfilter = public_factory(FunctionFilter, ".sql.expression.funcfilter")
# internal functions still being called from tests and the ORM,
# these might be better off in some other namespace
# old names for compatibility
_Executable = Executable
_BindParamClause = BindParameter
_Label = Label
_SelectBase = SelectBase
_BinaryExpression = BinaryExpression
_Cast = Cast
_Null = Null
_False = False_
_True = True_
_TextClause = TextClause
_UnaryExpression = UnaryExpression
_Case = Case
_Tuple = Tuple
_Over = Over
_TypeClause = TypeClause
_Extract = Extract
_Exists = Exists
_Grouping = Grouping
_FromGrouping = FromGrouping
_ScalarSelect = ScalarSelect

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
# sqlalchemy/naming.py
# Copyright (C) 2005-2022 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
"""Establish constraint and index naming conventions.
"""
import re
from . import events # noqa
from .elements import _NONE_NAME
from .elements import conv
from .schema import CheckConstraint
from .schema import Column
from .schema import Constraint
from .schema import ForeignKeyConstraint
from .schema import Index
from .schema import PrimaryKeyConstraint
from .schema import Table
from .schema import UniqueConstraint
from .. import event
from .. import exc
class ConventionDict(object):
def __init__(self, const, table, convention):
self.const = const
self._is_fk = isinstance(const, ForeignKeyConstraint)
self.table = table
self.convention = convention
self._const_name = const.name
def _key_table_name(self):
return self.table.name
def _column_X(self, idx, attrname):
if self._is_fk:
try:
fk = self.const.elements[idx]
except IndexError:
return ""
else:
return getattr(fk.parent, attrname)
else:
cols = list(self.const.columns)
try:
col = cols[idx]
except IndexError:
return ""
else:
return getattr(col, attrname)
def _key_constraint_name(self):
if self._const_name in (None, _NONE_NAME):
raise exc.InvalidRequestError(
"Naming convention including "
"%(constraint_name)s token requires that "
"constraint is explicitly named."
)
if not isinstance(self._const_name, conv):
self.const.name = None
return self._const_name
def _key_column_X_key(self, idx):
# note this method was missing before
# [ticket:3989], meaning tokens like ``%(column_0_key)s`` weren't
# working even though documented.
return self._column_X(idx, "key")
def _key_column_X_name(self, idx):
return self._column_X(idx, "name")
def _key_column_X_label(self, idx):
return self._column_X(idx, "_ddl_label")
def _key_referred_table_name(self):
fk = self.const.elements[0]
refs = fk.target_fullname.split(".")
if len(refs) == 3:
refschema, reftable, refcol = refs
else:
reftable, refcol = refs
return reftable
def _key_referred_column_X_name(self, idx):
fk = self.const.elements[idx]
# note that before [ticket:3989], this method was returning
# the specification for the :class:`.ForeignKey` itself, which normally
# would be using the ``.key`` of the column, not the name.
return fk.column.name
def __getitem__(self, key):
if key in self.convention:
return self.convention[key](self.const, self.table)
elif hasattr(self, "_key_%s" % key):
return getattr(self, "_key_%s" % key)()
else:
col_template = re.match(r".*_?column_(\d+)(_?N)?_.+", key)
if col_template:
idx = col_template.group(1)
multiples = col_template.group(2)
if multiples:
if self._is_fk:
elems = self.const.elements
else:
elems = list(self.const.columns)
tokens = []
for idx, elem in enumerate(elems):
attr = "_key_" + key.replace("0" + multiples, "X")
try:
tokens.append(getattr(self, attr)(idx))
except AttributeError:
raise KeyError(key)
sep = "_" if multiples.startswith("_") else ""
return sep.join(tokens)
else:
attr = "_key_" + key.replace(idx, "X")
idx = int(idx)
if hasattr(self, attr):
return getattr(self, attr)(idx)
raise KeyError(key)
_prefix_dict = {
Index: "ix",
PrimaryKeyConstraint: "pk",
CheckConstraint: "ck",
UniqueConstraint: "uq",
ForeignKeyConstraint: "fk",
}
def _get_convention(dict_, key):
for super_ in key.__mro__:
if super_ in _prefix_dict and _prefix_dict[super_] in dict_:
return dict_[_prefix_dict[super_]]
elif super_ in dict_:
return dict_[super_]
else:
return None
def _constraint_name_for_table(const, table):
metadata = table.metadata
convention = _get_convention(metadata.naming_convention, type(const))
if isinstance(const.name, conv):
return const.name
elif (
convention is not None
and not isinstance(const.name, conv)
and (
const.name is None
or "constraint_name" in convention
or const.name is _NONE_NAME
)
):
return conv(
convention
% ConventionDict(const, table, metadata.naming_convention)
)
elif convention is _NONE_NAME:
return None
@event.listens_for(
PrimaryKeyConstraint, "_sa_event_column_added_to_pk_constraint"
)
def _column_added_to_pk_constraint(pk_constraint, col):
if pk_constraint._implicit_generated:
# only operate upon the "implicit" pk constraint for now,
# as we have to force the name to None to reset it. the
# "implicit" constraint will only have a naming convention name
# if at all.
table = pk_constraint.table
pk_constraint.name = None
newname = _constraint_name_for_table(pk_constraint, table)
if newname:
pk_constraint.name = newname
@event.listens_for(Constraint, "after_parent_attach")
@event.listens_for(Index, "after_parent_attach")
def _constraint_name(const, table):
if isinstance(table, Column):
# this path occurs for a CheckConstraint linked to a Column
# for column-attached constraint, set another event
# to link the column attached to the table as this constraint
# associated with the table.
event.listen(
table,
"after_parent_attach",
lambda col, table: _constraint_name(const, table),
)
elif isinstance(table, Table):
if isinstance(const.name, conv) or const.name is _NONE_NAME:
return
newname = _constraint_name_for_table(const, table)
if newname:
const.name = newname

File diff suppressed because it is too large Load Diff

239
lib/sqlalchemy/sql/roles.py Normal file
View File

@@ -0,0 +1,239 @@
# sql/roles.py
# Copyright (C) 2005-2022 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
from .. import util
class SQLRole(object):
"""Define a "role" within a SQL statement structure.
Classes within SQL Core participate within SQLRole hierarchies in order
to more accurately indicate where they may be used within SQL statements
of all types.
.. versionadded:: 1.4
"""
allows_lambda = False
uses_inspection = False
class UsesInspection(object):
_post_inspect = None
uses_inspection = True
class AllowsLambdaRole(object):
allows_lambda = True
class HasCacheKeyRole(SQLRole):
_role_name = "Cacheable Core or ORM object"
class ExecutableOptionRole(SQLRole):
__slots__ = ()
_role_name = "ExecutionOption Core or ORM object"
class LiteralValueRole(SQLRole):
_role_name = "Literal Python value"
class ColumnArgumentRole(SQLRole):
_role_name = "Column expression"
class ColumnArgumentOrKeyRole(ColumnArgumentRole):
_role_name = "Column expression or string key"
class StrAsPlainColumnRole(ColumnArgumentRole):
_role_name = "Column expression or string key"
class ColumnListRole(SQLRole):
"""Elements suitable for forming comma separated lists of expressions."""
class TruncatedLabelRole(SQLRole):
_role_name = "String SQL identifier"
class ColumnsClauseRole(AllowsLambdaRole, UsesInspection, ColumnListRole):
_role_name = "Column expression or FROM clause"
@property
def _select_iterable(self):
raise NotImplementedError()
class LimitOffsetRole(SQLRole):
_role_name = "LIMIT / OFFSET expression"
class ByOfRole(ColumnListRole):
_role_name = "GROUP BY / OF / etc. expression"
class GroupByRole(AllowsLambdaRole, UsesInspection, ByOfRole):
# note there's a special case right now where you can pass a whole
# ORM entity to group_by() and it splits out. we may not want to keep
# this around
_role_name = "GROUP BY expression"
class OrderByRole(AllowsLambdaRole, ByOfRole):
_role_name = "ORDER BY expression"
class StructuralRole(SQLRole):
pass
class StatementOptionRole(StructuralRole):
_role_name = "statement sub-expression element"
class OnClauseRole(AllowsLambdaRole, StructuralRole):
_role_name = "SQL expression for ON clause"
class WhereHavingRole(OnClauseRole):
_role_name = "SQL expression for WHERE/HAVING role"
class ExpressionElementRole(SQLRole):
_role_name = "SQL expression element"
class ConstExprRole(ExpressionElementRole):
_role_name = "Constant True/False/None expression"
class LabeledColumnExprRole(ExpressionElementRole):
pass
class BinaryElementRole(ExpressionElementRole):
_role_name = "SQL expression element or literal value"
class InElementRole(SQLRole):
_role_name = (
"IN expression list, SELECT construct, or bound parameter object"
)
class JoinTargetRole(AllowsLambdaRole, UsesInspection, StructuralRole):
_role_name = (
"Join target, typically a FROM expression, or ORM "
"relationship attribute"
)
class FromClauseRole(ColumnsClauseRole, JoinTargetRole):
_role_name = "FROM expression, such as a Table or alias() object"
_is_subquery = False
@property
def _hide_froms(self):
raise NotImplementedError()
class StrictFromClauseRole(FromClauseRole):
# does not allow text() or select() objects
@property
def description(self):
raise NotImplementedError()
class AnonymizedFromClauseRole(StrictFromClauseRole):
# calls .alias() as a post processor
def _anonymous_fromclause(self, name=None, flat=False):
raise NotImplementedError()
class ReturnsRowsRole(SQLRole):
_role_name = (
"Row returning expression such as a SELECT, a FROM clause, or an "
"INSERT/UPDATE/DELETE with RETURNING"
)
class StatementRole(SQLRole):
_role_name = "Executable SQL or text() construct"
_propagate_attrs = util.immutabledict()
class SelectStatementRole(StatementRole, ReturnsRowsRole):
_role_name = "SELECT construct or equivalent text() construct"
def subquery(self):
raise NotImplementedError(
"All SelectStatementRole objects should implement a "
".subquery() method."
)
class HasCTERole(ReturnsRowsRole):
pass
class IsCTERole(SQLRole):
_role_name = "CTE object"
class CompoundElementRole(AllowsLambdaRole, SQLRole):
"""SELECT statements inside a CompoundSelect, e.g. UNION, EXTRACT, etc."""
_role_name = (
"SELECT construct for inclusion in a UNION or other set construct"
)
# TODO: are we using this?
class DMLRole(StatementRole):
pass
class DMLTableRole(FromClauseRole):
_role_name = "subject table for an INSERT, UPDATE or DELETE"
class DMLColumnRole(SQLRole):
_role_name = "SET/VALUES column expression or string key"
class DMLSelectRole(SQLRole):
"""A SELECT statement embedded in DML, typically INSERT from SELECT"""
_role_name = "SELECT statement or equivalent textual object"
class DDLRole(StatementRole):
pass
class DDLExpressionRole(StructuralRole):
_role_name = "SQL expression element for DDL constraint"
class DDLConstraintColumnRole(SQLRole):
_role_name = "String column name or column expression for DDL constraint"
class DDLReferredColumnRole(DDLConstraintColumnRole):
_role_name = (
"String column name or Column object for DDL foreign key constraint"
)

5268
lib/sqlalchemy/sql/schema.py Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1120
lib/sqlalchemy/sql/util.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,852 @@
# sql/visitors.py
# Copyright (C) 2005-2022 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
"""Visitor/traversal interface and library functions.
SQLAlchemy schema and expression constructs rely on a Python-centric
version of the classic "visitor" pattern as the primary way in which
they apply functionality. The most common use of this pattern
is statement compilation, where individual expression classes match
up to rendering methods that produce a string result. Beyond this,
the visitor system is also used to inspect expressions for various
information and patterns, as well as for the purposes of applying
transformations to expressions.
Examples of how the visit system is used can be seen in the source code
of for example the ``sqlalchemy.sql.util`` and the ``sqlalchemy.sql.compiler``
modules. Some background on clause adaption is also at
https://techspot.zzzeek.org/2008/01/23/expression-transformations/ .
"""
from collections import deque
import itertools
import operator
from .. import exc
from .. import util
from ..util import langhelpers
from ..util import symbol
__all__ = [
"iterate",
"traverse_using",
"traverse",
"cloned_traverse",
"replacement_traverse",
"Traversible",
"TraversibleType",
"ExternalTraversal",
"InternalTraversal",
]
def _generate_compiler_dispatch(cls):
"""Generate a _compiler_dispatch() external traversal on classes with a
__visit_name__ attribute.
"""
visit_name = cls.__visit_name__
if "_compiler_dispatch" in cls.__dict__:
# class has a fixed _compiler_dispatch() method.
# copy it to "original" so that we can get it back if
# sqlalchemy.ext.compiles overrides it.
cls._original_compiler_dispatch = cls._compiler_dispatch
return
if not isinstance(visit_name, util.compat.string_types):
raise exc.InvalidRequestError(
"__visit_name__ on class %s must be a string at the class level"
% cls.__name__
)
name = "visit_%s" % visit_name
getter = operator.attrgetter(name)
def _compiler_dispatch(self, visitor, **kw):
"""Look for an attribute named "visit_<visit_name>" on the
visitor, and call it with the same kw params.
"""
try:
meth = getter(visitor)
except AttributeError as err:
return visitor.visit_unsupported_compilation(self, err, **kw)
else:
return meth(self, **kw)
cls._compiler_dispatch = (
cls._original_compiler_dispatch
) = _compiler_dispatch
class TraversibleType(type):
"""Metaclass which assigns dispatch attributes to various kinds of
"visitable" classes.
Attributes include:
* The ``_compiler_dispatch`` method, corresponding to ``__visit_name__``.
This is called "external traversal" because the caller of each visit()
method is responsible for sub-traversing the inner elements of each
object. This is appropriate for string compilers and other traversals
that need to call upon the inner elements in a specific pattern.
* internal traversal collections ``_children_traversal``,
``_cache_key_traversal``, ``_copy_internals_traversal``, generated from
an optional ``_traverse_internals`` collection of symbols which comes
from the :class:`.InternalTraversal` list of symbols. This is called
"internal traversal" MARKMARK
"""
def __init__(cls, clsname, bases, clsdict):
if clsname != "Traversible":
if "__visit_name__" in clsdict:
_generate_compiler_dispatch(cls)
super(TraversibleType, cls).__init__(clsname, bases, clsdict)
class Traversible(util.with_metaclass(TraversibleType)):
"""Base class for visitable objects, applies the
:class:`.visitors.TraversibleType` metaclass.
"""
def __class_getitem__(cls, key):
# allow generic classes in py3.9+
return cls
@util.preload_module("sqlalchemy.sql.traversals")
def get_children(self, omit_attrs=(), **kw):
r"""Return immediate child :class:`.visitors.Traversible`
elements of this :class:`.visitors.Traversible`.
This is used for visit traversal.
\**kw may contain flags that change the collection that is
returned, for example to return a subset of items in order to
cut down on larger traversals, or to return child items from a
different context (such as schema-level collections instead of
clause-level).
"""
traversals = util.preloaded.sql_traversals
try:
traverse_internals = self._traverse_internals
except AttributeError:
# user-defined classes may not have a _traverse_internals
return []
dispatch = traversals._get_children.run_generated_dispatch
return itertools.chain.from_iterable(
meth(obj, **kw)
for attrname, obj, meth in dispatch(
self, traverse_internals, "_generated_get_children_traversal"
)
if attrname not in omit_attrs and obj is not None
)
class _InternalTraversalType(type):
def __init__(cls, clsname, bases, clsdict):
if cls.__name__ in ("InternalTraversal", "ExtendedInternalTraversal"):
lookup = {}
for key, sym in clsdict.items():
if key.startswith("dp_"):
visit_key = key.replace("dp_", "visit_")
sym_name = sym.name
assert sym_name not in lookup, sym_name
lookup[sym] = lookup[sym_name] = visit_key
if hasattr(cls, "_dispatch_lookup"):
lookup.update(cls._dispatch_lookup)
cls._dispatch_lookup = lookup
super(_InternalTraversalType, cls).__init__(clsname, bases, clsdict)
def _generate_dispatcher(visitor, internal_dispatch, method_name):
names = []
for attrname, visit_sym in internal_dispatch:
meth = visitor.dispatch(visit_sym)
if meth:
visit_name = ExtendedInternalTraversal._dispatch_lookup[visit_sym]
names.append((attrname, visit_name))
code = (
(" return [\n")
+ (
", \n".join(
" (%r, self.%s, visitor.%s)"
% (attrname, attrname, visit_name)
for attrname, visit_name in names
)
)
+ ("\n ]\n")
)
meth_text = ("def %s(self, visitor):\n" % method_name) + code + "\n"
# print(meth_text)
return langhelpers._exec_code_in_env(meth_text, {}, method_name)
class InternalTraversal(util.with_metaclass(_InternalTraversalType, object)):
r"""Defines visitor symbols used for internal traversal.
The :class:`.InternalTraversal` class is used in two ways. One is that
it can serve as the superclass for an object that implements the
various visit methods of the class. The other is that the symbols
themselves of :class:`.InternalTraversal` are used within
the ``_traverse_internals`` collection. Such as, the :class:`.Case`
object defines ``_traverse_internals`` as ::
_traverse_internals = [
("value", InternalTraversal.dp_clauseelement),
("whens", InternalTraversal.dp_clauseelement_tuples),
("else_", InternalTraversal.dp_clauseelement),
]
Above, the :class:`.Case` class indicates its internal state as the
attributes named ``value``, ``whens``, and ``else_``. They each
link to an :class:`.InternalTraversal` method which indicates the type
of datastructure referred towards.
Using the ``_traverse_internals`` structure, objects of type
:class:`.InternalTraversible` will have the following methods automatically
implemented:
* :meth:`.Traversible.get_children`
* :meth:`.Traversible._copy_internals`
* :meth:`.Traversible._gen_cache_key`
Subclasses can also implement these methods directly, particularly for the
:meth:`.Traversible._copy_internals` method, when special steps
are needed.
.. versionadded:: 1.4
"""
def dispatch(self, visit_symbol):
"""Given a method from :class:`.InternalTraversal`, return the
corresponding method on a subclass.
"""
name = self._dispatch_lookup[visit_symbol]
return getattr(self, name, None)
def run_generated_dispatch(
self, target, internal_dispatch, generate_dispatcher_name
):
try:
dispatcher = target.__class__.__dict__[generate_dispatcher_name]
except KeyError:
# most of the dispatchers are generated up front
# in sqlalchemy/sql/__init__.py ->
# traversals.py-> _preconfigure_traversals().
# this block will generate any remaining dispatchers.
dispatcher = self.generate_dispatch(
target.__class__, internal_dispatch, generate_dispatcher_name
)
return dispatcher(target, self)
def generate_dispatch(
self, target_cls, internal_dispatch, generate_dispatcher_name
):
dispatcher = _generate_dispatcher(
self, internal_dispatch, generate_dispatcher_name
)
# assert isinstance(target_cls, type)
setattr(target_cls, generate_dispatcher_name, dispatcher)
return dispatcher
dp_has_cache_key = symbol("HC")
"""Visit a :class:`.HasCacheKey` object."""
dp_has_cache_key_list = symbol("HL")
"""Visit a list of :class:`.HasCacheKey` objects."""
dp_clauseelement = symbol("CE")
"""Visit a :class:`_expression.ClauseElement` object."""
dp_fromclause_canonical_column_collection = symbol("FC")
"""Visit a :class:`_expression.FromClause` object in the context of the
``columns`` attribute.
The column collection is "canonical", meaning it is the originally
defined location of the :class:`.ColumnClause` objects. Right now
this means that the object being visited is a
:class:`_expression.TableClause`
or :class:`_schema.Table` object only.
"""
dp_clauseelement_tuples = symbol("CTS")
"""Visit a list of tuples which contain :class:`_expression.ClauseElement`
objects.
"""
dp_clauseelement_list = symbol("CL")
"""Visit a list of :class:`_expression.ClauseElement` objects.
"""
dp_clauseelement_tuple = symbol("CT")
"""Visit a tuple of :class:`_expression.ClauseElement` objects.
"""
dp_executable_options = symbol("EO")
dp_with_context_options = symbol("WC")
dp_fromclause_ordered_set = symbol("CO")
"""Visit an ordered set of :class:`_expression.FromClause` objects. """
dp_string = symbol("S")
"""Visit a plain string value.
Examples include table and column names, bound parameter keys, special
keywords such as "UNION", "UNION ALL".
The string value is considered to be significant for cache key
generation.
"""
dp_string_list = symbol("SL")
"""Visit a list of strings."""
dp_anon_name = symbol("AN")
"""Visit a potentially "anonymized" string value.
The string value is considered to be significant for cache key
generation.
"""
dp_boolean = symbol("B")
"""Visit a boolean value.
The boolean value is considered to be significant for cache key
generation.
"""
dp_operator = symbol("O")
"""Visit an operator.
The operator is a function from the :mod:`sqlalchemy.sql.operators`
module.
The operator value is considered to be significant for cache key
generation.
"""
dp_type = symbol("T")
"""Visit a :class:`.TypeEngine` object
The type object is considered to be significant for cache key
generation.
"""
dp_plain_dict = symbol("PD")
"""Visit a dictionary with string keys.
The keys of the dictionary should be strings, the values should
be immutable and hashable. The dictionary is considered to be
significant for cache key generation.
"""
dp_dialect_options = symbol("DO")
"""Visit a dialect options structure."""
dp_string_clauseelement_dict = symbol("CD")
"""Visit a dictionary of string keys to :class:`_expression.ClauseElement`
objects.
"""
dp_string_multi_dict = symbol("MD")
"""Visit a dictionary of string keys to values which may either be
plain immutable/hashable or :class:`.HasCacheKey` objects.
"""
dp_annotations_key = symbol("AK")
"""Visit the _annotations_cache_key element.
This is a dictionary of additional information about a ClauseElement
that modifies its role. It should be included when comparing or caching
objects, however generating this key is relatively expensive. Visitors
should check the "_annotations" dict for non-None first before creating
this key.
"""
dp_plain_obj = symbol("PO")
"""Visit a plain python object.
The value should be immutable and hashable, such as an integer.
The value is considered to be significant for cache key generation.
"""
dp_named_ddl_element = symbol("DD")
"""Visit a simple named DDL element.
The current object used by this method is the :class:`.Sequence`.
The object is only considered to be important for cache key generation
as far as its name, but not any other aspects of it.
"""
dp_prefix_sequence = symbol("PS")
"""Visit the sequence represented by :class:`_expression.HasPrefixes`
or :class:`_expression.HasSuffixes`.
"""
dp_table_hint_list = symbol("TH")
"""Visit the ``_hints`` collection of a :class:`_expression.Select`
object.
"""
dp_setup_join_tuple = symbol("SJ")
dp_memoized_select_entities = symbol("ME")
dp_statement_hint_list = symbol("SH")
"""Visit the ``_statement_hints`` collection of a
:class:`_expression.Select`
object.
"""
dp_unknown_structure = symbol("UK")
"""Visit an unknown structure.
"""
dp_dml_ordered_values = symbol("DML_OV")
"""Visit the values() ordered tuple list of an
:class:`_expression.Update` object."""
dp_dml_values = symbol("DML_V")
"""Visit the values() dictionary of a :class:`.ValuesBase`
(e.g. Insert or Update) object.
"""
dp_dml_multi_values = symbol("DML_MV")
"""Visit the values() multi-valued list of dictionaries of an
:class:`_expression.Insert` object.
"""
dp_propagate_attrs = symbol("PA")
"""Visit the propagate attrs dict. This hardcodes to the particular
elements we care about right now."""
class ExtendedInternalTraversal(InternalTraversal):
"""Defines additional symbols that are useful in caching applications.
Traversals for :class:`_expression.ClauseElement` objects only need to use
those symbols present in :class:`.InternalTraversal`. However, for
additional caching use cases within the ORM, symbols dealing with the
:class:`.HasCacheKey` class are added here.
"""
dp_ignore = symbol("IG")
"""Specify an object that should be ignored entirely.
This currently applies function call argument caching where some
arguments should not be considered to be part of a cache key.
"""
dp_inspectable = symbol("IS")
"""Visit an inspectable object where the return value is a
:class:`.HasCacheKey` object."""
dp_multi = symbol("M")
"""Visit an object that may be a :class:`.HasCacheKey` or may be a
plain hashable object."""
dp_multi_list = symbol("MT")
"""Visit a tuple containing elements that may be :class:`.HasCacheKey` or
may be a plain hashable object."""
dp_has_cache_key_tuples = symbol("HT")
"""Visit a list of tuples which contain :class:`.HasCacheKey`
objects.
"""
dp_inspectable_list = symbol("IL")
"""Visit a list of inspectable objects which upon inspection are
HasCacheKey objects."""
class ExternalTraversal(object):
"""Base class for visitor objects which can traverse externally using
the :func:`.visitors.traverse` function.
Direct usage of the :func:`.visitors.traverse` function is usually
preferred.
"""
__traverse_options__ = {}
def traverse_single(self, obj, **kw):
for v in self.visitor_iterator:
meth = getattr(v, "visit_%s" % obj.__visit_name__, None)
if meth:
return meth(obj, **kw)
def iterate(self, obj):
"""Traverse the given expression structure, returning an iterator
of all elements.
"""
return iterate(obj, self.__traverse_options__)
def traverse(self, obj):
"""Traverse and visit the given expression structure."""
return traverse(obj, self.__traverse_options__, self._visitor_dict)
@util.memoized_property
def _visitor_dict(self):
visitors = {}
for name in dir(self):
if name.startswith("visit_"):
visitors[name[6:]] = getattr(self, name)
return visitors
@property
def visitor_iterator(self):
"""Iterate through this visitor and each 'chained' visitor."""
v = self
while v:
yield v
v = getattr(v, "_next", None)
def chain(self, visitor):
"""'Chain' an additional ClauseVisitor onto this ClauseVisitor.
The chained visitor will receive all visit events after this one.
"""
tail = list(self.visitor_iterator)[-1]
tail._next = visitor
return self
class CloningExternalTraversal(ExternalTraversal):
"""Base class for visitor objects which can traverse using
the :func:`.visitors.cloned_traverse` function.
Direct usage of the :func:`.visitors.cloned_traverse` function is usually
preferred.
"""
def copy_and_process(self, list_):
"""Apply cloned traversal to the given list of elements, and return
the new list.
"""
return [self.traverse(x) for x in list_]
def traverse(self, obj):
"""Traverse and visit the given expression structure."""
return cloned_traverse(
obj, self.__traverse_options__, self._visitor_dict
)
class ReplacingExternalTraversal(CloningExternalTraversal):
"""Base class for visitor objects which can traverse using
the :func:`.visitors.replacement_traverse` function.
Direct usage of the :func:`.visitors.replacement_traverse` function is
usually preferred.
"""
def replace(self, elem):
"""Receive pre-copied elements during a cloning traversal.
If the method returns a new element, the element is used
instead of creating a simple copy of the element. Traversal
will halt on the newly returned element if it is re-encountered.
"""
return None
def traverse(self, obj):
"""Traverse and visit the given expression structure."""
def replace(elem):
for v in self.visitor_iterator:
e = v.replace(elem)
if e is not None:
return e
return replacement_traverse(obj, self.__traverse_options__, replace)
# backwards compatibility
Visitable = Traversible
VisitableType = TraversibleType
ClauseVisitor = ExternalTraversal
CloningVisitor = CloningExternalTraversal
ReplacingCloningVisitor = ReplacingExternalTraversal
def iterate(obj, opts=util.immutabledict()):
r"""Traverse the given expression structure, returning an iterator.
Traversal is configured to be breadth-first.
The central API feature used by the :func:`.visitors.iterate`
function is the
:meth:`_expression.ClauseElement.get_children` method of
:class:`_expression.ClauseElement` objects. This method should return all
the :class:`_expression.ClauseElement` objects which are associated with a
particular :class:`_expression.ClauseElement` object. For example, a
:class:`.Case` structure will refer to a series of
:class:`_expression.ColumnElement` objects within its "whens" and "else\_"
member variables.
:param obj: :class:`_expression.ClauseElement` structure to be traversed
:param opts: dictionary of iteration options. This dictionary is usually
empty in modern usage.
"""
yield obj
children = obj.get_children(**opts)
if not children:
return
stack = deque([children])
while stack:
t_iterator = stack.popleft()
for t in t_iterator:
yield t
stack.append(t.get_children(**opts))
def traverse_using(iterator, obj, visitors):
"""Visit the given expression structure using the given iterator of
objects.
:func:`.visitors.traverse_using` is usually called internally as the result
of the :func:`.visitors.traverse` function.
:param iterator: an iterable or sequence which will yield
:class:`_expression.ClauseElement`
structures; the iterator is assumed to be the
product of the :func:`.visitors.iterate` function.
:param obj: the :class:`_expression.ClauseElement`
that was used as the target of the
:func:`.iterate` function.
:param visitors: dictionary of visit functions. See :func:`.traverse`
for details on this dictionary.
.. seealso::
:func:`.traverse`
"""
for target in iterator:
meth = visitors.get(target.__visit_name__, None)
if meth:
meth(target)
return obj
def traverse(obj, opts, visitors):
"""Traverse and visit the given expression structure using the default
iterator.
e.g.::
from sqlalchemy.sql import visitors
stmt = select(some_table).where(some_table.c.foo == 'bar')
def visit_bindparam(bind_param):
print("found bound value: %s" % bind_param.value)
visitors.traverse(stmt, {}, {"bindparam": visit_bindparam})
The iteration of objects uses the :func:`.visitors.iterate` function,
which does a breadth-first traversal using a stack.
:param obj: :class:`_expression.ClauseElement` structure to be traversed
:param opts: dictionary of iteration options. This dictionary is usually
empty in modern usage.
:param visitors: dictionary of visit functions. The dictionary should
have strings as keys, each of which would correspond to the
``__visit_name__`` of a particular kind of SQL expression object, and
callable functions as values, each of which represents a visitor function
for that kind of object.
"""
return traverse_using(iterate(obj, opts), obj, visitors)
def cloned_traverse(obj, opts, visitors):
"""Clone the given expression structure, allowing modifications by
visitors.
Traversal usage is the same as that of :func:`.visitors.traverse`.
The visitor functions present in the ``visitors`` dictionary may also
modify the internals of the given structure as the traversal proceeds.
The central API feature used by the :func:`.visitors.cloned_traverse`
and :func:`.visitors.replacement_traverse` functions, in addition to the
:meth:`_expression.ClauseElement.get_children`
function that is used to achieve
the iteration, is the :meth:`_expression.ClauseElement._copy_internals`
method.
For a :class:`_expression.ClauseElement`
structure to support cloning and replacement
traversals correctly, it needs to be able to pass a cloning function into
its internal members in order to make copies of them.
.. seealso::
:func:`.visitors.traverse`
:func:`.visitors.replacement_traverse`
"""
cloned = {}
stop_on = set(opts.get("stop_on", []))
def deferred_copy_internals(obj):
return cloned_traverse(obj, opts, visitors)
def clone(elem, **kw):
if elem in stop_on:
return elem
else:
if id(elem) not in cloned:
if "replace" in kw:
newelem = kw["replace"](elem)
if newelem is not None:
cloned[id(elem)] = newelem
return newelem
cloned[id(elem)] = newelem = elem._clone(clone=clone, **kw)
newelem._copy_internals(clone=clone, **kw)
meth = visitors.get(newelem.__visit_name__, None)
if meth:
meth(newelem)
return cloned[id(elem)]
if obj is not None:
obj = clone(
obj, deferred_copy_internals=deferred_copy_internals, **opts
)
clone = None # remove gc cycles
return obj
def replacement_traverse(obj, opts, replace):
"""Clone the given expression structure, allowing element
replacement by a given replacement function.
This function is very similar to the :func:`.visitors.cloned_traverse`
function, except instead of being passed a dictionary of visitors, all
elements are unconditionally passed into the given replace function.
The replace function then has the option to return an entirely new object
which will replace the one given. If it returns ``None``, then the object
is kept in place.
The difference in usage between :func:`.visitors.cloned_traverse` and
:func:`.visitors.replacement_traverse` is that in the former case, an
already-cloned object is passed to the visitor function, and the visitor
function can then manipulate the internal state of the object.
In the case of the latter, the visitor function should only return an
entirely different object, or do nothing.
The use case for :func:`.visitors.replacement_traverse` is that of
replacing a FROM clause inside of a SQL structure with a different one,
as is a common use case within the ORM.
"""
cloned = {}
stop_on = {id(x) for x in opts.get("stop_on", [])}
def deferred_copy_internals(obj):
return replacement_traverse(obj, opts, replace)
def clone(elem, **kw):
if (
id(elem) in stop_on
or "no_replacement_traverse" in elem._annotations
):
return elem
else:
newelem = replace(elem)
if newelem is not None:
stop_on.add(id(newelem))
return newelem
else:
# base "already seen" on id(), not hash, so that we don't
# replace an Annotated element with its non-annotated one, and
# vice versa
id_elem = id(elem)
if id_elem not in cloned:
if "replace" in kw:
newelem = kw["replace"](elem)
if newelem is not None:
cloned[id_elem] = newelem
return newelem
cloned[id_elem] = newelem = elem._clone(**kw)
newelem._copy_internals(clone=clone, **kw)
return cloned[id_elem]
if obj is not None:
obj = clone(
obj, deferred_copy_internals=deferred_copy_internals, **opts
)
clone = None # remove gc cycles
return obj