SQLAlchemy 2.1.0rc2__py3-none-any.whl → 2.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqlalchemy/__init__.py CHANGED
@@ -285,7 +285,7 @@ from .types import Uuid as Uuid
285
285
  from .types import VARBINARY as VARBINARY
286
286
  from .types import VARCHAR as VARCHAR
287
287
 
288
- __version__ = "2.1.0rc2"
288
+ __version__ = "2.1.1"
289
289
 
290
290
 
291
291
  def __go(lcls: Any) -> None:
@@ -101,9 +101,6 @@ class MSDialect_mssqlpython(MSDialect):
101
101
  }
102
102
  )
103
103
 
104
- # used by pyodbc _ms_numeric_pyodbc class
105
- _need_decimal_fix = True
106
-
107
104
  colspecs = util.update_copy(
108
105
  MSDialect.colspecs,
109
106
  {
@@ -117,6 +114,18 @@ class MSDialect_mssqlpython(MSDialect):
117
114
  if not enable_pooling and self.dbapi is not None:
118
115
  self.loaded_dbapi.pooling(enabled=False)
119
116
 
117
+ # used by pyodbc _ms_numeric_pyodbc class. mssql-python 1.15
118
+ # binds every Decimal as SQL_NUMERIC; prior versions bound Decimal
119
+ # values within the MONEY range as VARCHAR, so that very large and
120
+ # very small values needed to be passed as strings as well. On
121
+ # 1.15 those strings are instead coerced to the NUMERIC type of
122
+ # the Decimal values alongside them, which overflows.
123
+ # a version which can't be determined is assumed to be an old one
124
+ version = self._dbapi_version_or_none
125
+ self._need_decimal_fix = bool(self.dbapi) and (
126
+ version is None or version < (1, 15)
127
+ )
128
+
120
129
  @classmethod
121
130
  def import_dbapi(cls) -> DBAPIModule:
122
131
  return __import__("mssql_python")
@@ -204,7 +213,7 @@ class MSDialect_mssqlpython(MSDialect):
204
213
  def retrieve_dbapi_version(
205
214
  self, dbapi: interfaces.DBAPIModule
206
215
  ) -> util.VersionInfo:
207
- return util.parse_version_string(dbapi.version)
216
+ return util.parse_version_string(getattr(dbapi, "__version__", None))
208
217
 
209
218
  def _get_server_version_info(self, connection):
210
219
  vers = connection.exec_driver_sql("select @@version").scalar()
@@ -600,6 +600,7 @@ from ...connectors.asyncio import AsyncAdapt_dbapi_connection
600
600
  from ...connectors.asyncio import AsyncAdapt_dbapi_cursor
601
601
  from ...connectors.asyncio import AsyncAdapt_dbapi_module
602
602
  from ...connectors.asyncio import AsyncAdapt_dbapi_ss_cursor
603
+ from ...connectors.asyncio import AsyncAdapt_terminate
603
604
  from ...engine import default
604
605
  from ...util import await_
605
606
 
@@ -773,7 +774,9 @@ class AsyncAdapt_oracledb_ss_cursor(
773
774
  self._cursor = None # type: ignore
774
775
 
775
776
 
776
- class AsyncAdapt_oracledb_connection(AsyncAdapt_dbapi_connection):
777
+ class AsyncAdapt_oracledb_connection(
778
+ AsyncAdapt_terminate, AsyncAdapt_dbapi_connection
779
+ ):
777
780
  _connection: AsyncConnection
778
781
  __slots__ = ()
779
782
 
@@ -838,6 +841,12 @@ class AsyncAdapt_oracledb_connection(AsyncAdapt_dbapi_connection):
838
841
  def tpc_rollback(self, *args: Any, **kwargs: Any) -> Any:
839
842
  return await_(self._connection.tpc_rollback(*args, **kwargs))
840
843
 
844
+ async def _terminate_graceful_close(self) -> None:
845
+ await self._connection.close()
846
+
847
+ def _terminate_force_close(self) -> None:
848
+ self._connection.terminate()
849
+
841
850
 
842
851
  class OracledbAdaptDBAPI(AsyncAdapt_dbapi_module):
843
852
  def __init__(self, oracledb) -> None:
@@ -883,6 +892,14 @@ class OracleDialectAsync_oracledb(OracleDialect_oracledb):
883
892
 
884
893
  minimum_dbapi_version = util.VersionInfo((2, 0, 1))
885
894
 
895
+ def __init__(self, **kwargs):
896
+ super().__init__(**kwargs)
897
+
898
+ self.has_terminate = (
899
+ self._dbapi_version_or_none is not None
900
+ and self._dbapi_version_or_none >= (26, 0, 0)
901
+ )
902
+
886
903
  # thick_mode mode is not supported by asyncio, oracledb will raise
887
904
  @classmethod
888
905
  def import_dbapi(cls):
@@ -893,6 +910,12 @@ class OracleDialectAsync_oracledb(OracleDialect_oracledb):
893
910
  def get_driver_connection(self, connection):
894
911
  return connection._connection
895
912
 
913
+ def do_terminate(self, dbapi_connection) -> None:
914
+ if self.has_terminate:
915
+ dbapi_connection.terminate()
916
+ else:
917
+ super().do_terminate(dbapi_connection)
918
+
896
919
 
897
920
  dialect = OracleDialect_oracledb
898
921
  dialect_async = OracleDialectAsync_oracledb
@@ -1179,6 +1179,12 @@ construct, the DBAPI's "autocommit" mode must be used::
1179
1179
  PostgreSQL Index Reflection
1180
1180
  ---------------------------
1181
1181
 
1182
+ Notes on Index reflection
1183
+
1184
+
1185
+ Implicit UNIQUE index for UNIQUE CONSTRAINT
1186
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1187
+
1182
1188
  The PostgreSQL database creates a UNIQUE INDEX implicitly whenever the
1183
1189
  UNIQUE CONSTRAINT construct is used. When inspecting a table using
1184
1190
  :class:`_reflection.Inspector`, the :meth:`_reflection.Inspector.get_indexes`
@@ -1189,8 +1195,38 @@ two constructs distinctly; in the case of the index, the key
1189
1195
  detected as mirroring a constraint. When performing reflection using
1190
1196
  ``Table(..., autoload_with=engine)``, the UNIQUE INDEX is **not** returned
1191
1197
  in :attr:`_schema.Table.indexes` when it is detected as mirroring a
1192
- :class:`.UniqueConstraint` in the :attr:`_schema.Table.constraints` collection
1193
- .
1198
+ :class:`.UniqueConstraint` in the :attr:`_schema.Table.constraints` collection.
1199
+
1200
+ .. _postgresql_invalid_index_reflection:
1201
+
1202
+ Invalid indexes
1203
+ ^^^^^^^^^^^^^^^
1204
+
1205
+ Indexes marked "invalid" by PostgreSQL (i.e. an index that exists in the
1206
+ catalog, but is ignored by the query planner because it may be incomplete,
1207
+ while still being maintained on writes) remain present in reflection. The
1208
+ :meth:`_reflection.Inspector.get_indexes` and
1209
+ :meth:`_reflection.Inspector.get_multi_indexes` methods include
1210
+ ``postgresql_invalid=True`` in the index's ``dialect_options`` dictionary when
1211
+ ``pg_index.indisvalid`` is false. Valid indexes omit this flag.
1212
+
1213
+ When reflecting a :class:`_schema.Table`, the flag, if present, is stored
1214
+ separately from DDL options on each reflected :class:`.Index`::
1215
+
1216
+ table = Table("my_table", MetaData(), autoload_with=engine)
1217
+ for index in table.indexes:
1218
+ if index.reflect_only_elements["postgresql"].get("invalid"):
1219
+ print(index.name)
1220
+
1221
+ The :attr:`.Index.reflect_only_elements` mapping is read-only. These values are
1222
+ not included in :attr:`.Index.dialect_kwargs` and do not affect DDL.
1223
+
1224
+ An invalid index may be left by a failed ``CREATE INDEX CONCURRENTLY``, may
1225
+ still be building, or may be a partitioned index awaiting attachment of its
1226
+ partition indexes. This flag reports the state at reflection time; it does not
1227
+ request an index rebuild.
1228
+
1229
+ .. versionadded:: 2.1
1194
1230
 
1195
1231
  Special Reflection Options
1196
1232
  --------------------------
@@ -1946,6 +1982,7 @@ from ...sql import functions
1946
1982
  from ...sql import roles
1947
1983
  from ...sql import sqltypes
1948
1984
  from ...sql import util as sql_util
1985
+ from ...sql.base import DialectKWArgConst
1949
1986
  from ...sql.compiler import InsertmanyvaluesSentinelOpts
1950
1987
  from ...sql.visitors import InternalTraversal
1951
1988
  from ...types import BIGINT
@@ -3666,6 +3703,7 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
3666
3703
  "with": {},
3667
3704
  "tablespace": None,
3668
3705
  "nulls_not_distinct": None,
3706
+ "invalid": DialectKWArgConst.REFLECTED_ONLY,
3669
3707
  },
3670
3708
  ),
3671
3709
  (
@@ -5269,6 +5307,7 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
5269
5307
  pg_catalog.pg_index.c.indrelid,
5270
5308
  pg_catalog.pg_class.c.relname,
5271
5309
  pg_catalog.pg_index.c.indisunique,
5310
+ pg_catalog.pg_index.c.indisvalid,
5272
5311
  pg_catalog.pg_constraint.c.conrelid.is_not(None).label(
5273
5312
  "has_constraint"
5274
5313
  ),
@@ -5475,6 +5514,9 @@ class PGDialect(default._BackendsMultiReflection, default.DefaultDialect):
5475
5514
  "indnullsnotdistinct"
5476
5515
  ]
5477
5516
 
5517
+ if not row["indisvalid"]:
5518
+ dialect_options["postgresql_invalid"] = True
5519
+
5478
5520
  if dialect_options:
5479
5521
  index["dialect_options"] = dialect_options
5480
5522
 
@@ -560,7 +560,7 @@ class ReflectedIndex(TypedDict):
560
560
  """
561
561
 
562
562
  dialect_options: NotRequired[Dict[str, Any]]
563
- """Additional dialect-specific options detected for this index"""
563
+ """Additional dialect-specific options detected for this index."""
564
564
 
565
565
 
566
566
  class ReflectedTableComment(TypedDict):
@@ -1259,6 +1259,17 @@ class Dialect(EventTarget):
1259
1259
  here is so that third-party dialects that haven't yet implemented this
1260
1260
  feature continue to function in the old way.
1261
1261
 
1262
+ Arguments that represent database state without any corresponding DDL,
1263
+ but may nonetheless appear in a reflected set of arguments that should
1264
+ be ignored by the construct, may make use of the
1265
+ :attr:`.DialectKWArgConst.REFLECTED_ONLY` token as the default value.
1266
+ Arguments passed to the schema object on such a key will be passed along
1267
+ into the separate read-only collection
1268
+ :attr:`.DialectKWArgs.reflect_only_elements` and should be omitted from
1269
+ DDL rendering.
1270
+
1271
+ .. versionadded:: 2.1 Added :class:`.DialectKWArgConst`.
1272
+
1262
1273
  .. seealso::
1263
1274
 
1264
1275
  :class:`.DialectKWArgs` - implementing base class which consumes
@@ -642,6 +642,7 @@ def composite(
642
642
  return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
643
643
  comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
644
644
  active_history: bool = False,
645
+ column_template: Optional[str] = None,
645
646
  init: Union[_NoArg, bool] = _NoArg.NO_ARG,
646
647
  repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
647
648
  default: Optional[Any] = _NoArg.NO_ARG,
@@ -667,6 +668,7 @@ def composite(
667
668
  return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
668
669
  comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
669
670
  active_history: bool = False,
671
+ column_template: Optional[str] = None,
670
672
  init: Union[_NoArg, bool] = _NoArg.NO_ARG,
671
673
  repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
672
674
  default: Optional[Any] = _NoArg.NO_ARG,
@@ -691,6 +693,7 @@ def composite(
691
693
  return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
692
694
  comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
693
695
  active_history: bool = False,
696
+ column_template: Optional[str] = None,
694
697
  init: Union[_NoArg, bool] = _NoArg.NO_ARG,
695
698
  repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
696
699
  default: Optional[Any] = _NoArg.NO_ARG,
@@ -716,6 +719,7 @@ def composite(
716
719
  return_none_on: Union[_NoArg, None, Callable[..., bool]] = _NoArg.NO_ARG,
717
720
  comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None,
718
721
  active_history: bool = False,
722
+ column_template: Optional[str] = None,
719
723
  init: Union[_NoArg, bool] = _NoArg.NO_ARG,
720
724
  repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002
721
725
  default: Optional[Any] = _NoArg.NO_ARG,
@@ -785,6 +789,18 @@ def composite(
785
789
  :class:`.Composite.Comparator` which provides custom SQL
786
790
  clause generation for comparison operations.
787
791
 
792
+ :param column_template: A string template such as ``"person_%s"``,
793
+ containing exactly one ``%s`` placeholder, that's used to generate
794
+ column names for fields of a dataclass :paramref:`.composite.class_`
795
+ that don't otherwise have an explicit name. Only supported for
796
+ dataclass-based composite classes.
797
+
798
+ .. seealso::
799
+
800
+ :ref:`composite_column_template`
801
+
802
+ .. versionadded:: 2.1
803
+
788
804
  :param doc:
789
805
  optional string that will be applied as the doc on the
790
806
  class-bound descriptor.
@@ -852,6 +868,7 @@ def composite(
852
868
  raiseload=raiseload,
853
869
  comparator_factory=comparator_factory,
854
870
  active_history=active_history,
871
+ column_template=column_template,
855
872
  info=info,
856
873
  doc=doc,
857
874
  )
@@ -208,6 +208,7 @@ class CompositeProperty(
208
208
 
209
209
  composite_class: Union[Type[_CC], Callable[..., _CC]]
210
210
  attrs: Tuple[_CompositeAttrType[Any], ...]
211
+ column_template: Optional[str]
211
212
 
212
213
  _generated_composite_accessor: CallableReference[
213
214
  Optional[Callable[[_CC], Tuple[Any, ...]]]
@@ -229,6 +230,7 @@ class CompositeProperty(
229
230
  deferred: bool = False,
230
231
  group: Optional[str] = None,
231
232
  comparator_factory: Optional[Type[Comparator[_CC]]] = None,
233
+ column_template: Optional[str] = None,
232
234
  info: Optional[_InfoType] = None,
233
235
  **kwargs: Any,
234
236
  ):
@@ -242,6 +244,17 @@ class CompositeProperty(
242
244
  self.composite_class = _class_or_attr # type: ignore[assignment]
243
245
  self.attrs = attrs
244
246
 
247
+ if column_template is not None:
248
+ try:
249
+ column_template % "x"
250
+ except (TypeError, ValueError) as te:
251
+ raise sa_exc.ArgumentError(
252
+ f"column_template {column_template!r} is not a valid "
253
+ "template; expected a string containing exactly one "
254
+ "'%s' placeholder"
255
+ ) from te
256
+ self.column_template = column_template
257
+
245
258
  self.return_none_on = return_none_on
246
259
  self.active_history = active_history
247
260
  self.deferred = deferred
@@ -428,6 +441,11 @@ class CompositeProperty(
428
441
  decl_scan, registry, cls, originating_module, key
429
442
  )
430
443
  else:
444
+ if self.column_template is not None:
445
+ raise sa_exc.ArgumentError(
446
+ "column_template is only supported when composite_class "
447
+ "is a dataclass"
448
+ )
431
449
  for attr in self.attrs:
432
450
  if (
433
451
  isinstance(attr, (MappedColumn, schema.Column))
@@ -492,8 +510,12 @@ class CompositeProperty(
492
510
  f"{self.composite_class.__name__} {len(insp.parameters)}"
493
511
  )
494
512
  if attr is None:
495
- # fill in missing attr spots with empty MappedColumn
496
- attr = MappedColumn()
513
+ # fill in missing attr spots with empty MappedColumn,
514
+ # or one named from column_template if present
515
+ if self.column_template is not None:
516
+ attr = MappedColumn(self.column_template % param.name)
517
+ else:
518
+ attr = MappedColumn()
497
519
  self.attrs += (attr,)
498
520
 
499
521
  if isinstance(attr, MappedColumn):
@@ -66,6 +66,13 @@ if TYPE_CHECKING:
66
66
  from ..sql.elements import ColumnElement
67
67
 
68
68
 
69
+ # sentinel for _LoadLazyAttribute.extra_criteria indicating that the loader
70
+ # option had no additional criteria. the attribute is otherwise a single
71
+ # element returned by Load._generate_extra_criteria(), which has no
72
+ # meaningful boolean value of its own.
73
+ _NO_EXTRA_CRITERIA = None
74
+
75
+
69
76
  def _register_attribute(
70
77
  prop,
71
78
  mapper,
@@ -1233,7 +1240,7 @@ class _LazyLoader(
1233
1240
  (
1234
1241
  loadopt._generate_extra_criteria(context)
1235
1242
  if loadopt._extra_criteria
1236
- else None
1243
+ else _NO_EXTRA_CRITERIA
1237
1244
  ),
1238
1245
  ),
1239
1246
  key,
@@ -1281,7 +1288,7 @@ class _LoadLazyAttribute:
1281
1288
  self.extra_criteria = extra_criteria
1282
1289
 
1283
1290
  def __getstate__(self):
1284
- if self.extra_criteria is not None:
1291
+ if self.extra_criteria is not _NO_EXTRA_CRITERIA:
1285
1292
  util.warn(
1286
1293
  "Can't reliably serialize a lazyload() option that "
1287
1294
  "contains additional criteria; please use eager loading "
@@ -1291,7 +1298,7 @@ class _LoadLazyAttribute:
1291
1298
  "key": self.key,
1292
1299
  "strategy_key": self.strategy_key,
1293
1300
  "loadopt": self.loadopt,
1294
- "extra_criteria": (),
1301
+ "extra_criteria": _NO_EXTRA_CRITERIA,
1295
1302
  }
1296
1303
 
1297
1304
  def __call__(self, state, passive=attributes.PASSIVE_OFF):
sqlalchemy/orm/util.py CHANGED
@@ -10,6 +10,7 @@ from __future__ import annotations
10
10
 
11
11
  import enum
12
12
  import functools
13
+ import itertools
13
14
  import re
14
15
  import types
15
16
  import typing
@@ -633,6 +634,41 @@ class ORMAdapter(sql_util.ColumnAdapter):
633
634
  return not entity or entity.isa(self.mapper) or self.mapper.isa(entity)
634
635
 
635
636
 
637
+ def _is_alias_of_selectable(
638
+ selectable: FromClause, target: FromClause
639
+ ) -> bool:
640
+ """Return True if ``selectable`` is ``target``, or a plain alias of it,
641
+ including a flat alias of a join of the same structure and join types."""
642
+
643
+ if selectable is target or (
644
+ isinstance(selectable, expression.Alias)
645
+ and selectable.element is target
646
+ ):
647
+ return True
648
+
649
+ return all(
650
+ elem is tgt
651
+ or (isinstance(elem, expression.Alias) and elem.element is tgt)
652
+ or (
653
+ # zip_longest() pads the shorter of the two with None
654
+ elem is not None
655
+ and tgt is not None
656
+ and elem._is_join
657
+ and tgt._is_join
658
+ and elem.isouter == tgt.isouter
659
+ and elem.full == tgt.full
660
+ )
661
+ or (
662
+ isinstance(elem, expression.FromGrouping)
663
+ and isinstance(tgt, expression.FromGrouping)
664
+ )
665
+ for elem, tgt in itertools.zip_longest(
666
+ sql_util.surface_selectables(selectable),
667
+ sql_util.surface_selectables(target),
668
+ )
669
+ )
670
+
671
+
636
672
  class AliasedClass(
637
673
  inspection.Inspectable["AliasedInsp[_O]"], ORMColumnsClauseRole[_O]
638
674
  ):
@@ -718,9 +754,38 @@ class AliasedClass(
718
754
  nest_adapters = False
719
755
 
720
756
  if alias is None:
721
- if insp.is_aliased_class and insp.selectable._is_subquery:
722
- alias = insp.selectable.alias()
723
- else:
757
+ if insp_is_aliased_class(insp):
758
+ if insp._is_with_polymorphic:
759
+ # polymorphic arguments are only passed along with an
760
+ # explicit selectable, by with_polymorphic() and
761
+ # AliasedInsp._merge_with()
762
+ assert (
763
+ not with_polymorphic_mappers
764
+ and with_polymorphic_discriminator is None
765
+ and not represents_outer_join
766
+ )
767
+
768
+ # aliased() of a with_polymorphic() carries along its
769
+ # polymorphic configuration, #13584
770
+ with_polymorphic_mappers = insp.with_polymorphic_mappers
771
+ with_polymorphic_discriminator = insp.polymorphic_on
772
+ represents_outer_join = insp.represents_outer_join
773
+
774
+ if not _is_alias_of_selectable(
775
+ insp.selectable, mapper._with_polymorphic_selectable
776
+ ):
777
+ # aliased() of an aliased() that refers to a subquery,
778
+ # CTE, with_polymorphic() or other selectable; alias
779
+ # that selectable rather than the mapped table,
780
+ # #13583, #13584
781
+ alias = insp.selectable._anonymous_fromclause(
782
+ name=name, flat=flat
783
+ )
784
+ adapt_on_names = adapt_on_names or insp._adapt_on_names
785
+ elif insp.selectable._is_subquery:
786
+ alias = insp.selectable.alias()
787
+
788
+ if alias is None:
724
789
  alias = (
725
790
  mapper._with_polymorphic_selectable._anonymous_fromclause(
726
791
  name=name,
@@ -925,6 +990,7 @@ class AliasedInsp(
925
990
  _adapter: ORMAdapter
926
991
  with_polymorphic_mappers: Sequence[Mapper[Any]]
927
992
  _with_polymorphic_entities: Sequence[AliasedInsp[Any]]
993
+ _adapt_on_names: bool
928
994
 
929
995
  _weak_entity: weakref.ref[AliasedClass[_O]]
930
996
  """the AliasedClass that refers to this AliasedInsp"""
@@ -2344,6 +2410,29 @@ def _cleanup_mapped_str_annotation(
2344
2410
  return annotation
2345
2411
 
2346
2412
 
2413
+ def _unresolved_annotation_is_mapped(
2414
+ raw_annotation: _AnnotationScanType,
2415
+ ) -> bool:
2416
+ """given an un-resolvable annotation, guess if it's ``Mapped[]``.
2417
+
2418
+ Used only to decide whether a ``NameError`` raised while de-stringifying
2419
+ is worth reporting as a mapping error. The annotation may be a plain
2420
+ string (``__future__`` annotations / explicitly quoted), a ``ForwardRef``
2421
+ (:pep:`649` deferred annotations on Python 3.14 and above), or an
2422
+ already-resolved object.
2423
+
2424
+ """
2425
+ if isinstance(raw_annotation, str):
2426
+ return "Mapped[" in raw_annotation
2427
+ elif isinstance(raw_annotation, typing.ForwardRef):
2428
+ return "Mapped[" in raw_annotation.__forward_arg__
2429
+ else:
2430
+ origin = typing.get_origin(raw_annotation)
2431
+ return isinstance(origin, type) and issubclass(
2432
+ origin, _MappedAnnotationBase
2433
+ )
2434
+
2435
+
2347
2436
  def _extract_mapped_subtype(
2348
2437
  raw_annotation: Optional[_AnnotationScanType],
2349
2438
  cls: type,
@@ -2390,7 +2479,7 @@ def _extract_mapped_subtype(
2390
2479
  "module level. See chained stack trace for more hints."
2391
2480
  ) from ce
2392
2481
  except NameError as ne:
2393
- if raiseerr and "Mapped[" in raw_annotation: # type: ignore[operator]
2482
+ if raiseerr and _unresolved_annotation_is_mapped(raw_annotation):
2394
2483
  raise orm_exc.MappedAnnotationError(
2395
2484
  f"Could not interpret annotation {raw_annotation}. "
2396
2485
  "Check that it uses names that are correctly imported at the "
sqlalchemy/sql/base.py CHANGED
@@ -377,6 +377,31 @@ def _cloned_difference(a: Iterable[_CLE], b: Iterable[_CLE]) -> Set[_CLE]:
377
377
  }
378
378
 
379
379
 
380
+ class DialectKWArgConst(Enum):
381
+ """Constants for dialect argument defaults in
382
+ :attr:`.DefaultDialect.construct_arguments`.
383
+
384
+ """
385
+
386
+ REFLECTED_ONLY = 1
387
+ """Mark a dialect argument as database state reported by reflection.
388
+
389
+ Applies to any construct that takes part in
390
+ :attr:`.DefaultDialect.construct_arguments`, such as :class:`.Table`,
391
+ :class:`.Column`, :class:`.Index` or :class:`.CheckConstraint`. The
392
+ value is kept in the construct's read-only
393
+ :attr:`.DialectKWArgs.reflect_only_elements` mapping, separate from the
394
+ options used to generate DDL.
395
+
396
+ .. seealso::
397
+
398
+ :attr:`.DialectKWArgs.reflect_only_elements`
399
+
400
+ .. versionadded:: 2.1
401
+
402
+ """
403
+
404
+
380
405
  class _DialectArgView(MutableMapping[str, Any]):
381
406
  """A dictionary view of dialect-level arguments in the form
382
407
  <dialectname>_<argument_name>.
@@ -436,6 +461,46 @@ class _DialectArgView(MutableMapping[str, Any]):
436
461
  )
437
462
 
438
463
 
464
+ class _ReflectOnlyView(Mapping[str, Mapping[str, Any]]):
465
+ """A read-only dictionary view of reflection-only dialect-level
466
+ arguments, keyed to <dialectname>, then <argument_name>.
467
+
468
+ A dialect name that has no reflection-only arguments present returns
469
+ an empty mapping.
470
+
471
+ """
472
+
473
+ __slots__ = ("obj",)
474
+
475
+ def __init__(self, obj: DialectKWArgs) -> None:
476
+ self.obj = obj
477
+
478
+ def __getitem__(self, key: str) -> Mapping[str, Any]:
479
+ # check membership first so that the lookup does not attempt
480
+ # to load a dialect
481
+ if key in self.obj.dialect_options:
482
+ return self.obj.dialect_options[key]._reflect_only_elements
483
+ else:
484
+ return EMPTY_DICT
485
+
486
+ def __contains__(self, key: object) -> bool:
487
+ return bool(
488
+ isinstance(key, str)
489
+ and key in self.obj.dialect_options
490
+ and self.obj.dialect_options[key]._reflect_only_elements
491
+ )
492
+
493
+ def __len__(self) -> int:
494
+ return sum(1 for _ in self)
495
+
496
+ def __iter__(self) -> Generator[str, None, None]:
497
+ return (
498
+ dialect_name
499
+ for dialect_name, args in self.obj.dialect_options.items()
500
+ if args._reflect_only_elements
501
+ )
502
+
503
+
439
504
  class _DialectArgDict(MutableMapping[str, Any]):
440
505
  """A dictionary view of dialect-level arguments for a specific
441
506
  dialect.
@@ -448,6 +513,10 @@ class _DialectArgDict(MutableMapping[str, Any]):
448
513
  def __init__(self) -> None:
449
514
  self._non_defaults: Dict[str, Any] = {}
450
515
  self._defaults: Dict[str, Any] = {}
516
+ self._reflection_only_keys: FrozenSet[str] = util.EMPTY_SET
517
+ self._reflect_only_elements: util.immutabledict[str, Any] = (
518
+ util.EMPTY_DICT
519
+ )
451
520
 
452
521
  def __len__(self) -> int:
453
522
  return len(set(self._non_defaults).union(self._defaults))
@@ -462,7 +531,12 @@ class _DialectArgDict(MutableMapping[str, Any]):
462
531
  return self._defaults[key]
463
532
 
464
533
  def __setitem__(self, key: str, value: Any) -> None:
465
- self._non_defaults[key] = value
534
+ if key in self._reflection_only_keys:
535
+ self._reflect_only_elements = self._reflect_only_elements.union(
536
+ {key: value}
537
+ )
538
+ else:
539
+ self._non_defaults[key] = value
466
540
 
467
541
  def __delitem__(self, key: str) -> None:
468
542
  del self._non_defaults[key]
@@ -525,6 +599,11 @@ class DialectKWArgs:
525
599
  return else_
526
600
 
527
601
  if argument_name in registry.get(self.__class__, {}):
602
+ if (
603
+ argument_name
604
+ in self.dialect_options[dialect.name]._reflection_only_keys
605
+ ):
606
+ return else_
528
607
  if (
529
608
  deprecated_fallback is None
530
609
  or dialect.name == deprecated_fallback
@@ -646,6 +725,60 @@ class DialectKWArgs:
646
725
  """A synonym for :attr:`.DialectKWArgs.dialect_kwargs`."""
647
726
  return self.dialect_kwargs
648
727
 
728
+ @property
729
+ def reflect_only_elements(self) -> Mapping[str, Mapping[str, Any]]:
730
+ """A read-only collection of dialect-specific database state
731
+ reported by reflection, separate from DDL options.
732
+
733
+ Holds values for arguments whose
734
+ :attr:`.DefaultDialect.construct_arguments` default is
735
+ :attr:`.DialectKWArgConst.REFLECTED_ONLY`. Like
736
+ :attr:`.DialectKWArgs.dialect_options`, this is a two-level nested
737
+ collection keyed to ``<dialect_name>`` and ``<argument_name>``; a
738
+ dialect name with no values present returns an empty mapping, for
739
+ example::
740
+
741
+ invalid = my_index.reflect_only_elements["postgresql"].get("invalid")
742
+
743
+ These values are not included in
744
+ :attr:`.DialectKWArgs.dialect_options` or
745
+ :attr:`.DialectKWArgs.dialect_kwargs` and take no part in DDL
746
+ compilation. They are carried along to copies of the object, such
747
+ as those produced by :meth:`.Table.to_metadata`.
748
+
749
+ .. versionadded:: 2.1
750
+
751
+ .. seealso::
752
+
753
+ :attr:`.DialectKWArgConst.REFLECTED_ONLY`
754
+
755
+ """ # noqa: E501
756
+ return _ReflectOnlyView(self)
757
+
758
+ @property
759
+ def _reflect_only_kwargs(self) -> Dict[str, Any]:
760
+ """The contents of :attr:`.DialectKWArgs.reflect_only_elements` in
761
+ flat ``<dialect>_<argument>`` form, suitable to be passed to the
762
+ constructor of a copy of this object."""
763
+
764
+ return {
765
+ f"{dialect_name}_{key}": value
766
+ for dialect_name, elements in self.reflect_only_elements.items()
767
+ for key, value in elements.items()
768
+ }
769
+
770
+ def _copy_reflect_only_elements(self, other: DialectKWArgs) -> None:
771
+ """Copy the contents of :attr:`.DialectKWArgs.reflect_only_elements`
772
+ onto ``other``, a copy of this object."""
773
+
774
+ if "dialect_options" not in self.__dict__:
775
+ return
776
+ for dialect_name, args in self.dialect_options.items():
777
+ if args._reflect_only_elements:
778
+ other.dialect_options[dialect_name]._reflect_only_elements = (
779
+ args._reflect_only_elements
780
+ )
781
+
649
782
  _kw_registry: util.PopulateDict[str, Optional[Dict[Any, Any]]] = (
650
783
  util.PopulateDict(_kw_reg_for_dialect)
651
784
  )
@@ -661,6 +794,13 @@ class DialectKWArgs:
661
794
  for cls in reversed(cls.__mro__):
662
795
  if cls in construct_arg_dictionary:
663
796
  d._defaults.update(construct_arg_dictionary[cls])
797
+ d._reflection_only_keys = frozenset(
798
+ key
799
+ for key, value in d._defaults.items()
800
+ if value is DialectKWArgConst.REFLECTED_ONLY
801
+ )
802
+ for key in d._reflection_only_keys:
803
+ del d._defaults[key]
664
804
  return d
665
805
 
666
806
  @util.memoized_property
@@ -676,10 +816,18 @@ class DialectKWArgs:
676
816
 
677
817
  .. versionadded:: 0.9.2
678
818
 
819
+ Arguments a dialect declares as
820
+ :attr:`.DialectKWArgConst.REFLECTED_ONLY` are not included in this
821
+ collection; they are available from
822
+ :attr:`.DialectKWArgs.reflect_only_elements`.
823
+
679
824
  .. seealso::
680
825
 
681
826
  :attr:`.DialectKWArgs.dialect_kwargs` - flat dictionary form
682
827
 
828
+ :attr:`.DialectKWArgs.reflect_only_elements` - database state
829
+ reported by reflection
830
+
683
831
  """
684
832
 
685
833
  return util.PopulateDict(self._kw_reg_for_dialect_cls)
@@ -714,6 +862,8 @@ class DialectKWArgs:
714
862
  if (
715
863
  "*" not in construct_arg_dictionary
716
864
  and arg_name not in construct_arg_dictionary
865
+ and arg_name
866
+ not in construct_arg_dictionary._reflection_only_keys
717
867
  ):
718
868
  raise exc.ArgumentError(
719
869
  "Argument %r is not accepted by "
sqlalchemy/sql/schema.py CHANGED
@@ -265,6 +265,9 @@ class SchemaItem(SchemaVisitable):
265
265
  def _schema_item_copy(self, schema_item: _SI) -> _SI:
266
266
  if "info" in self.__dict__:
267
267
  schema_item.info = self.info.copy()
268
+ if isinstance(self, DialectKWArgs):
269
+ assert isinstance(schema_item, DialectKWArgs)
270
+ self._copy_reflect_only_elements(schema_item)
268
271
  schema_item.dispatch._update(self.dispatch)
269
272
  return schema_item
270
273
 
@@ -1766,6 +1769,7 @@ class Table(
1766
1769
  ],
1767
1770
  _table=table,
1768
1771
  **index.kwargs,
1772
+ **index._reflect_only_kwargs,
1769
1773
  )
1770
1774
  return self._schema_item_copy(table)
1771
1775
 
@@ -4655,6 +4659,7 @@ class Sequence(HasSchemaAttr, IdentityOptions, DefaultGenerator):
4655
4659
  for_update=self.for_update,
4656
4660
  **self._as_dict(),
4657
4661
  **self.dialect_kwargs,
4662
+ **self._reflect_only_kwargs,
4658
4663
  )
4659
4664
 
4660
4665
  def _set_parent(self, parent: SchemaEventTarget, **kw: Any) -> None:
@@ -1654,6 +1654,19 @@ class Join(roles.DMLTableRole, FromClause[_KeyColCC_co]):
1654
1654
  """
1655
1655
  return Select(self.left, self.right).select_from(self)
1656
1656
 
1657
+ @staticmethod
1658
+ def _flat_element_name(
1659
+ name: Optional[str], element: FromClause
1660
+ ) -> Optional[str]:
1661
+ if name and isinstance(element, NamedFromClause):
1662
+ if isinstance(element.name, _anonymous_label):
1663
+ # an anonymous name can't be embedded within a new name;
1664
+ # alias the element anonymously, #13583
1665
+ return None
1666
+ return f"{name}_{element.name}"
1667
+ else:
1668
+ return name
1669
+
1657
1670
  @util.preload_module("sqlalchemy.sql.util")
1658
1671
  def _anonymous_fromclause(
1659
1672
  self, name: Optional[str] = None, flat: bool = False
@@ -1663,17 +1676,11 @@ class Join(roles.DMLTableRole, FromClause[_KeyColCC_co]):
1663
1676
  if isinstance(self.left, (FromGrouping, Join)):
1664
1677
  left_name = name # will recurse
1665
1678
  else:
1666
- if name and isinstance(self.left, NamedFromClause):
1667
- left_name = f"{name}_{self.left.name}"
1668
- else:
1669
- left_name = name
1679
+ left_name = self._flat_element_name(name, self.left)
1670
1680
  if isinstance(self.right, (FromGrouping, Join)):
1671
1681
  right_name = name # will recurse
1672
1682
  else:
1673
- if name and isinstance(self.right, NamedFromClause):
1674
- right_name = f"{name}_{self.right.name}"
1675
- else:
1676
- right_name = name
1683
+ right_name = self._flat_element_name(name, self.right)
1677
1684
  left_a, right_a = (
1678
1685
  self.left._anonymous_fromclause(name=left_name, flat=flat),
1679
1686
  self.right._anonymous_fromclause(name=right_name, flat=flat),
@@ -2291,7 +2298,10 @@ class CTE(
2291
2298
  name=name,
2292
2299
  recursive=self.recursive,
2293
2300
  nesting=self.nesting,
2294
- _cte_alias=self,
2301
+ # an alias of an alias refers to the original CTE, #13583
2302
+ _cte_alias=(
2303
+ self._cte_alias if self._cte_alias is not None else self
2304
+ ),
2295
2305
  _prefixes=self._prefixes,
2296
2306
  _suffixes=self._suffixes,
2297
2307
  )
@@ -483,6 +483,19 @@ class SuiteRequirements(Requirements):
483
483
  "%(database)s %(does_support)s 'UPDATE ... RETURNING'",
484
484
  )
485
485
 
486
+ @property
487
+ def update_returning_dialect_not_supported(self):
488
+ """target database rejects UPDATE ... RETURNING when the dialect
489
+ reports that ``update_returning`` is not supported.
490
+
491
+ This is normally the case; it's false only where a server has
492
+ gained UPDATE ... RETURNING that the dialect doesn't implement
493
+ yet, so that the statement runs rather than raising.
494
+
495
+ """
496
+
497
+ return exclusions.open()
498
+
486
499
  @property
487
500
  def insert_executemany_returning(self):
488
501
  """target platform supports RETURNING when INSERT is used with
@@ -1698,6 +1711,12 @@ class SuiteRequirements(Requirements):
1698
1711
  lambda: not util.freethreading, "GIL-enabled build needed"
1699
1712
  )
1700
1713
 
1714
+ @property
1715
+ def freethreading(self):
1716
+ return exclusions.only_if(
1717
+ lambda: util.freethreading, "free-threaded build needed"
1718
+ )
1719
+
1701
1720
  @property
1702
1721
  def is64bit(self):
1703
1722
  return exclusions.only_if(lambda: util.is64bit, "64bit required")
@@ -707,6 +707,7 @@ class ReturningGuardsTest(fixtures.TablesTest):
707
707
  stmt, True, "id", connection.dialect.insert_executemany_returning
708
708
  )
709
709
 
710
+ @testing.requires.update_returning_dialect_not_supported
710
711
  def test_update_single(self, connection, run_stmt):
711
712
  t = self.tables.t
712
713
 
@@ -100,6 +100,43 @@ class SimpleUpdateDeleteTest(fixtures.TablesTest):
100
100
  ),
101
101
  )
102
102
 
103
+ @testing.variation("unchanged", ["some", "all"])
104
+ @testing.requires.update_returning
105
+ def test_update_returning_unchanged_rows(self, connection, unchanged):
106
+ """test that UPDATE..RETURNING returns all rows matched by the WHERE
107
+ clause, including those where the new value is the same as the
108
+ existing value.
109
+
110
+ """
111
+ t = self.tables.plain_pk
112
+
113
+ if unchanged.some:
114
+ # WHERE matches 2, 1 row changed
115
+ stmt = t.update().where(t.c.id.in_([2, 3]))
116
+ elif unchanged.all:
117
+ # WHERE matches 1, 0 rows changed
118
+ stmt = t.update().where(t.c.id == 3)
119
+ else:
120
+ unchanged.fail()
121
+
122
+ r = connection.execute(
123
+ stmt.returning(t.c.id, t.c.data), dict(data="d3")
124
+ )
125
+
126
+ if unchanged.some:
127
+ eq_(sorted(r.all()), [(2, "d3"), (3, "d3")])
128
+ else:
129
+ eq_(r.all(), [(3, "d3")])
130
+
131
+ eq_(
132
+ connection.execute(t.select().order_by(t.c.id)).fetchall(),
133
+ (
134
+ [(1, "d1"), (2, "d3"), (3, "d3")]
135
+ if unchanged.some
136
+ else [(1, "d1"), (2, "d2"), (3, "d3")]
137
+ ),
138
+ )
139
+
103
140
  @testing.variation("criteria", ["rows", "norows", "emptyin"])
104
141
  @testing.requires.delete_returning
105
142
  def test_delete_returning(self, connection, criteria):
@@ -404,7 +404,13 @@ def _remove_linecache_entry(filename: str, entry: _LinecacheEntry) -> None:
404
404
  linecache within exec_code_in_env.
405
405
 
406
406
  """
407
- linecache_cache = _linecache_cache_getter()
407
+ try:
408
+ linecache_cache = _linecache_cache_getter()
409
+ except AttributeError:
410
+ # linecache.cache is gone or is no longer a dict; as this runs
411
+ # from a finalizer, an exception here would only surface as an
412
+ # unraisable error
413
+ return
408
414
 
409
415
  if linecache_cache.get(filename) is entry:
410
416
  linecache_cache.pop(filename, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: SQLAlchemy
3
- Version: 2.1.0rc2
3
+ Version: 2.1.1
4
4
  Summary: Database Abstraction Library
5
5
  Author-email: Mike Bayer <mike_mp@zzzcomputing.com>
6
6
  License-Expression: MIT
@@ -86,28 +86,6 @@ Requires-Dist: sqlalchemy[asyncio]; extra == "aiosqlite"
86
86
  Requires-Dist: aiosqlite; extra == "aiosqlite"
87
87
  Provides-Extra: sqlcipher
88
88
  Requires-Dist: sqlcipher3_binary; extra == "sqlcipher"
89
- Provides-Extra: mssql-pymssql
90
- Requires-Dist: sqlalchemy[mssql-pymssql]; extra == "mssql-pymssql"
91
- Provides-Extra: mssql-pyodbc
92
- Requires-Dist: sqlalchemy[mssql-pyodbc]; extra == "mssql-pyodbc"
93
- Provides-Extra: mysql-connector
94
- Requires-Dist: sqlalchemy[mysql-connector]; extra == "mysql-connector"
95
- Provides-Extra: mariadb-connector
96
- Requires-Dist: sqlalchemy[mariadb-connector]; extra == "mariadb-connector"
97
- Provides-Extra: oracle-oracledb
98
- Requires-Dist: sqlalchemy[oracle-oracledb]; extra == "oracle-oracledb"
99
- Provides-Extra: postgresql-pg8000
100
- Requires-Dist: sqlalchemy[postgresql-pg8000]; extra == "postgresql-pg8000"
101
- Provides-Extra: postgresql-asyncpg
102
- Requires-Dist: sqlalchemy[postgresql-asyncpg]; extra == "postgresql-asyncpg"
103
- Provides-Extra: postgresql-psycopg2binary
104
- Requires-Dist: sqlalchemy[postgresql-psycopg2binary]; extra == "postgresql-psycopg2binary"
105
- Provides-Extra: postgresql-psycopg2cffi
106
- Requires-Dist: sqlalchemy[postgresql-psycopg2cffi]; extra == "postgresql-psycopg2cffi"
107
- Provides-Extra: postgresql-psycopg
108
- Requires-Dist: sqlalchemy[postgresql-psycopg]; extra == "postgresql-psycopg"
109
- Provides-Extra: postgresql-psycopgbinary
110
- Requires-Dist: sqlalchemy[postgresql-psycopgbinary]; extra == "postgresql-psycopgbinary"
111
89
  Dynamic: license-file
112
90
 
113
91
  SQLAlchemy
@@ -1,4 +1,4 @@
1
- sqlalchemy/__init__.py,sha256=ZF4-5aXataCikWPmdUgorkau2S8Q4Qm8wff1v43c3do,13565
1
+ sqlalchemy/__init__.py,sha256=ujjm1qYHLGW9VikKFzIo5e1rkEv3dyvpvw9r8X2htw8,13562
2
2
  sqlalchemy/events.py,sha256=vEDW04X_PMhMw5y0_95tbEzht3wua8_gnkMBJstYNRw,525
3
3
  sqlalchemy/exc.py,sha256=CKyzx9WSeWTYxueZLAtTFpZTNKSon0YlpGt9v-C8dl8,27503
4
4
  sqlalchemy/inspection.py,sha256=2mcgybcOVuQDB2bSIhwidrMteP9qOpEbmehyT2lrnt8,5661
@@ -18,7 +18,7 @@ sqlalchemy/dialects/mssql/aioodbc.py,sha256=RS9TZI4cDa9My9YHQbWW2nD3lUBx-s8aQZ7G
18
18
  sqlalchemy/dialects/mssql/base.py,sha256=w_R8bOhMkDa6qRQEyqbcRepL9RF2ZaP8jmovMtJ7Kac,159925
19
19
  sqlalchemy/dialects/mssql/information_schema.py,sha256=xoO2mKqq99GBN9osJaaYjLw3VQg4YB8rFrh5K6gGfaI,10544
20
20
  sqlalchemy/dialects/mssql/json.py,sha256=SgcLWK-xmy_5FCzgxoz2Xa8pnJemVFCuiNQ6aFApHyk,5338
21
- sqlalchemy/dialects/mssql/mssqlpython.py,sha256=eE8xmSOOpFSpBcdN5poKTXQjHh4eqSHfld4J3xRd_sE,7224
21
+ sqlalchemy/dialects/mssql/mssqlpython.py,sha256=Boe6dsrabesUauvFUDIHeFXDXLBh3qi2pgqYCqI__eI,7832
22
22
  sqlalchemy/dialects/mssql/provision.py,sha256=KUTOtIMDDSi9R3uoIkTJiz_L3qUg6rFvDY6RzbgQsl4,6884
23
23
  sqlalchemy/dialects/mssql/pymssql.py,sha256=gIeA411WXjMKBg_Zg8pPc4alci3OqF9yTzVqwUaRCEY,4215
24
24
  sqlalchemy/dialects/mssql/pyodbc.py,sha256=hcyGiZb1dJUsegbwdYRjotr9UPvPZkXx9Bt-oorN8hc,25100
@@ -47,7 +47,7 @@ sqlalchemy/dialects/oracle/base.py,sha256=TDl2u3tL6QzbDHTbrmnltQhxgqOvrG9lVdLzV4
47
47
  sqlalchemy/dialects/oracle/cx_oracle.py,sha256=ITGIIUnkWFLm9jb4Z7VD1UrZGebVDORrvFY_ZzLPOqM,63932
48
48
  sqlalchemy/dialects/oracle/dictionary.py,sha256=hotIZctkY58oepLJ3NYQxWb44oMOurxsXgD2yhe3neg,19519
49
49
  sqlalchemy/dialects/oracle/json.py,sha256=JingOTIOeoiN9ymSFK7K5RnUefKzEgQzb5RlTNR8hfY,6218
50
- sqlalchemy/dialects/oracle/oracledb.py,sha256=YmwzKyC6KrwlBbd-URGiF6H8y9jAn1yI5ROKx7zTdWw,32268
50
+ sqlalchemy/dialects/oracle/oracledb.py,sha256=uFfhyZazx2NcIwiTxeSy570LbJP6MV_GDjH0qAzP0Vk,32944
51
51
  sqlalchemy/dialects/oracle/provision.py,sha256=SiDKY7lVY5DsEMI1HEsoQe1cvzSL6AdrrliQtRonLgc,10685
52
52
  sqlalchemy/dialects/oracle/types.py,sha256=iVayov9nyK1MCIS26BvawokAn4PwIXIvdgH3j-JB56E,10894
53
53
  sqlalchemy/dialects/oracle/vector.py,sha256=v7zf88XX7ICORNuycw3da1d5G7KHmYytNtU6pI9Mv-4,10922
@@ -55,7 +55,7 @@ sqlalchemy/dialects/postgresql/__init__.py,sha256=uqvgSydDm7mbLeLtuARI8AXFkcHPCk
55
55
  sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=3btoOFqAcFmkectNE1Vgs7cOv4ccF28xIQZIDEYwOWA,7178
56
56
  sqlalchemy/dialects/postgresql/array.py,sha256=fHA_e2ez1SGHXa4BrXxbwCKMEkrDxn2GFzWJOjZrM0c,17907
57
57
  sqlalchemy/dialects/postgresql/asyncpg.py,sha256=IE2FNLURUBGvsofgGpqCJ6sx8Qd_alUsOz2Mb0YkeVk,42376
58
- sqlalchemy/dialects/postgresql/base.py,sha256=KVqJULdkTEiTD2XlLygkx7-7n0MVSFrtNFUtjY1Bbk4,212184
58
+ sqlalchemy/dialects/postgresql/base.py,sha256=lxkSsXXsKcEGbQwGXVrF7lY2Vn5_iEetJ3T7gxrrYk0,213886
59
59
  sqlalchemy/dialects/postgresql/bitstring.py,sha256=-AowJ4DqlcT0PcFXML44TU4VND_hFuvtkwqUgKBcE1I,10603
60
60
  sqlalchemy/dialects/postgresql/dml.py,sha256=ckxPBWuAXCRKkSFvdmwEppvirvczFY-voniqgC3X6DA,12959
61
61
  sqlalchemy/dialects/postgresql/ext.py,sha256=ZgdhLnWDYcm3RQ0XlUtrsW3WF1YRcGmzEK-8sdg16wc,19603
@@ -90,7 +90,7 @@ sqlalchemy/engine/create.py,sha256=7G0aidSUYtghZjyv7C19TZcv_3o3pS62UXj-sRgsWVs,3
90
90
  sqlalchemy/engine/cursor.py,sha256=B5NmeqIEsOiPU8QaE4dXh7s1cSp2_5erukysuKs7ipo,85474
91
91
  sqlalchemy/engine/default.py,sha256=nwcP1u060d0UpKWUnpTqZNyeTX_5zsbJnEyS9v_M5Wg,94262
92
92
  sqlalchemy/engine/events.py,sha256=d-BzEd6M8dG8q22w3N6RX98cDcrANTgYyPjb5oFQznE,37475
93
- sqlalchemy/engine/interfaces.py,sha256=uCYsffQTxLc4yw8MtCwnhClHEfdghTIZWVtc4sRYcjo,121749
93
+ sqlalchemy/engine/interfaces.py,sha256=JU4s2sK_LPKYM9Bqegb_eoAdUbTHO0-zSTN_sdtHwCU,122305
94
94
  sqlalchemy/engine/mock.py,sha256=i9J-z0NsL78VlqC85MRgo5SNNQU256UtEqficO3DCwY,4155
95
95
  sqlalchemy/engine/processors.py,sha256=uh6xb1ajvSzJufQDG2DmmMeP0VbgaeSHZOJPBSN3mwU,2710
96
96
  sqlalchemy/engine/reflection.py,sha256=vbRdfQ00pekzzdC3mwL6gwZ8iFnsVIbiawvDzoXTXBc,77136
@@ -129,7 +129,7 @@ sqlalchemy/ext/declarative/extensions.py,sha256=xcTpvgQo_DetyXNZtvnSZRaNTczVYIUh
129
129
  sqlalchemy/future/__init__.py,sha256=9pngQPAhogCThkP0WMqp43MYiMHQ8usQekk0IBhrUYw,513
130
130
  sqlalchemy/future/engine.py,sha256=CNIeZz-03GSBylYCTTam_Yk5kHD0EprfTxaNEgQc-0I,495
131
131
  sqlalchemy/orm/__init__.py,sha256=VhzsKd_oKslCrurAbQii-0IJ4wuDngQnLwnfZhb80GI,8791
132
- sqlalchemy/orm/_orm_constructors.py,sha256=ngXunJA_6WlDs-E8BaS2iYG5n_uEQpyfVy4fzdlXj3k,107250
132
+ sqlalchemy/orm/_orm_constructors.py,sha256=73M6QqtNY8H2NgwMfUjooo8eoPP8DvyIC9wgCiWO44g,107879
133
133
  sqlalchemy/orm/_typing.py,sha256=dj7GE9yxelGlP5XeJXAH3VHNawd6eBxcJyoQht0tVS8,5012
134
134
  sqlalchemy/orm/attributes.py,sha256=SJLVDunnq00povV2vfZq_b7c9wTWv0PSaV7W7Gx9YmM,93774
135
135
  sqlalchemy/orm/base.py,sha256=W8nHDIrPiFsRwiePCiSPRYLepUgM_gTsFi2UgvuFyeI,28350
@@ -140,7 +140,7 @@ sqlalchemy/orm/context.py,sha256=F1poIa-NftQTO_cNUfSnbJKjbPQs9kWy6tBEyznTD_8,120
140
140
  sqlalchemy/orm/decl_api.py,sha256=gK5IGcbAXMdCN6SzprewMhfbXiZe1yO1Ghv2y2z3EHA,78721
141
141
  sqlalchemy/orm/decl_base.py,sha256=IQzfu1cySobZg0-Boy3HQs11rO-hYrU1D6q6ERIXOPc,87150
142
142
  sqlalchemy/orm/dependency.py,sha256=gqUUJi7LMF2RIEvq7hw_w-WfbpW8CXzSaDW5QTW5NlQ,47739
143
- sqlalchemy/orm/descriptor_props.py,sha256=JI6I8frxACHtNIPdxTYBNfTMcbvRK1lOaqkCKpsA_0Q,41430
143
+ sqlalchemy/orm/descriptor_props.py,sha256=2R4NUuPIyinL5RrTbOAYebb3ZJNJLnT6_J2QCWy0nEM,42407
144
144
  sqlalchemy/orm/dynamic.py,sha256=7wyr0eM7xIRqWQk6jcoHjqGYatORwImRbrysJhQaGm8,9968
145
145
  sqlalchemy/orm/evaluator.py,sha256=DkKS71DYkzZRtxXRh-8pCyup_X7Xfbi9FjWHceTicOA,12352
146
146
  sqlalchemy/orm/events.py,sha256=fiDZRyN3-ikjkQEOypuADlkc_Jb9NXZEdd_wIAiikrI,131318
@@ -160,11 +160,11 @@ sqlalchemy/orm/scoping.py,sha256=eGXEv3sthVrBau1t7XCy-Irq9DA1uc7vpqw0IulAwjk,811
160
160
  sqlalchemy/orm/session.py,sha256=6-nextPWqVjwA7hW79oozm6pXbGHXF7Js3HLfJcgyUQ,203522
161
161
  sqlalchemy/orm/state.py,sha256=IxVE17w39HuK_U3vYKEGry-TE53ptfdCVuZmHjKn2MM,38600
162
162
  sqlalchemy/orm/state_changes.py,sha256=zbtHp9OaZhU80uTQSPyiDyQuCOQZsYil4mdiRa0Lvrw,6809
163
- sqlalchemy/orm/strategies.py,sha256=RPUiG0tvUt_gBPZo2Q0sFDVDXULt0idb4kbMGm8wxKI,124067
163
+ sqlalchemy/orm/strategies.py,sha256=PSEGq2fvV8YECRp03L5LSlsIMMFgoUGug-quSU0ksPQ,124396
164
164
  sqlalchemy/orm/strategy_options.py,sha256=Wzfd8hB-Xqd5eubYNfIfcsjzdd9DOFpsI0V4cvHUZ6k,89057
165
165
  sqlalchemy/orm/sync.py,sha256=j1v5g0M_0_cSKiv6UEek-DhPWAL9eSpc4hY9Z14c988,5785
166
166
  sqlalchemy/orm/unitofwork.py,sha256=ewxWWxVZw62LndJL0K_L3VFaACe_9m7ub0w-j2vyIjU,27135
167
- sqlalchemy/orm/util.py,sha256=CEiDtniU_ZKILpHr80rOptdRvjoecB3XKnFO9wcP3N4,83424
167
+ sqlalchemy/orm/util.py,sha256=hyHPD38nPxcJ7OpoJMMHW4kG4hWx-xj9OXTtY2D-gDs,86884
168
168
  sqlalchemy/orm/writeonly.py,sha256=LAbF6qIkV6agnH6f9piLFdsPvdewLsvhEagNebd-Kig,23096
169
169
  sqlalchemy/pool/__init__.py,sha256=h3EJgaBrR5w7aTqbf9cH8j3qf64jKj4fGe7nQ61jKNg,1714
170
170
  sqlalchemy/pool/base.py,sha256=ZRhJfpRiCBE8yqlrDPY5_NgiASxZ711FSHM3T6NtRLA,53716
@@ -181,7 +181,7 @@ sqlalchemy/sql/_typing.py,sha256=jDHln_-yp5AqbFY_B1uyFwuWAxrYF_iTJZ-KAb54tgI,140
181
181
  sqlalchemy/sql/_util_cy.pxd,sha256=JEkdMfO03ulZoZSalWA0goLog-TxIfEuXkZZadW0ZGg,349
182
182
  sqlalchemy/sql/_util_cy.py,sha256=vluLhp88b-vWkXIuZb971L9oRao7mlMDHk-DUGKGgIA,3871
183
183
  sqlalchemy/sql/annotation.py,sha256=gkFPOq3qGo6fahIxTwXgxCdcEyiD8W4CH0q01d1E0mo,18487
184
- sqlalchemy/sql/base.py,sha256=WaVrLBZxiniheZM4K8YgEjw-ebzumV01ApKouYslEGo,93319
184
+ sqlalchemy/sql/base.py,sha256=1xkh7mgC25GVZS8tczZbwDAxRvdumoaW3UPrJXbynbQ,98615
185
185
  sqlalchemy/sql/cache_key.py,sha256=OihJqOVsdU0F_IShr0FIptTRHtIlM1UIZ_5LR8LQd0Q,28872
186
186
  sqlalchemy/sql/coercions.py,sha256=XyV6wyiIHHD6ViGdZhLMQ8OVWNk4_LyBvv15t504UYI,39144
187
187
  sqlalchemy/sql/compiler.py,sha256=7xY9nqfs7_svpZqFNjxF4KOh2sVJtBE0ws1_XuQTlv0,299616
@@ -197,8 +197,8 @@ sqlalchemy/sql/lambdas.py,sha256=msXSQmXp2sVWDalooJBcm5xKXdLjSlf5vyNX8vdOq3A,494
197
197
  sqlalchemy/sql/naming.py,sha256=qBLfM2BizVlnFrDTcZ2AzTiUMml281BiOHRD-Qw1uXg,6705
198
198
  sqlalchemy/sql/operators.py,sha256=91HJboxRkDFNHx9JlpO9hsss7wqTfeoANOxjMFszTt0,87215
199
199
  sqlalchemy/sql/roles.py,sha256=QG4iNsYMCqjnMP3wt-5lEKxQZIh3uD2yVhurcS-z7VM,7857
200
- sqlalchemy/sql/schema.py,sha256=gwxTWZCMNYgDoWO6Wz8IzyjG0gBCb7rS243caC3xff8,262222
201
- sqlalchemy/sql/selectable.py,sha256=HasW5kz-dFy2eJq9_NZp-uV3RyZOFAJoBmgjvnfm-x0,257265
200
+ sqlalchemy/sql/schema.py,sha256=55oc5r8Mqtkcr2ker8kHjlBSUSO3QhwyyqrG2gaFgu4,262469
201
+ sqlalchemy/sql/selectable.py,sha256=xadnLUCBQFChbp2xMGjZrwXz86iOpUPBMGF8aPxYcgY,257659
202
202
  sqlalchemy/sql/sqltypes.py,sha256=Qa9Sl6iuLx0pB73O0LNgr_fauY8rGl_f9dU5wbOwth8,138409
203
203
  sqlalchemy/sql/traversals.py,sha256=xLBzv1qpTUjBTiTGMBvPUvl1UPffYSWMxV4wHgOM4i4,34355
204
204
  sqlalchemy/sql/type_api.py,sha256=ZWQkgF2vH61G4qNfv2cREPOUXeM5o0QaOHWkb1cmILo,88622
@@ -217,7 +217,7 @@ sqlalchemy/testing/pickleable.py,sha256=gXgySNI39dUcuNY6e-vM9HFKxQ7Uva7NVZgeYyNY
217
217
  sqlalchemy/testing/profiles_file.py,sha256=sxwF-c6Wd6IBEMHpU1QSdsouyCX7b-4EWWsTTdD8m4w,11349
218
218
  sqlalchemy/testing/profiling.py,sha256=pCeurQNGnsyMHotKuaT8GM8Aq_p1JADaIXPsWsvEJ9w,9159
219
219
  sqlalchemy/testing/provision.py,sha256=pOMK51WaXJyWAh48HbBjgywDPLk02pSS7rHQqoMLulI,18628
220
- sqlalchemy/testing/requirements.py,sha256=8DWnBaSsTESvXVF3QfFTZAMZKtro4CMKfgDQbcPb93I,57095
220
+ sqlalchemy/testing/requirements.py,sha256=gX6WD4YhhOSF1f8Hmc3Fl0odNQKqXlJuAxO4PK1G1c0,57705
221
221
  sqlalchemy/testing/schema.py,sha256=49x-TXARL2k8oIiaSzyGxXG5fh7vJOuBoUv5h9YTfKo,5360
222
222
  sqlalchemy/testing/util.py,sha256=vbd6v3shfmdxqnqbL0K_hg-yi-ou5Fe9FpkUZeCEyUk,15809
223
223
  sqlalchemy/testing/warnings.py,sha256=j8GWiP-yraCW0Ig5iJxAa5ekN82ZWqcQWyR-BUb4MZA,1546
@@ -233,7 +233,7 @@ sqlalchemy/testing/plugin/pytestplugin.py,sha256=swDvlVLofRYz3Q-w-svvHgdWIPoS_bv
233
233
  sqlalchemy/testing/suite/__init__.py,sha256=jLYLmFQ6k0Fn7N2iPrbXwzJMKDSe4oMdmD-AC6yu3ws,726
234
234
  sqlalchemy/testing/suite/test_cte.py,sha256=7AnV9dR9h76JWY0yWuyeQT-5GQsYbNMz3q2SG8IA_R4,7276
235
235
  sqlalchemy/testing/suite/test_ddl.py,sha256=03nV2s8lfcCw4Czq6pkHl0ZGho8KyFe_C-f0HthP3B0,13010
236
- sqlalchemy/testing/suite/test_dialect.py,sha256=b8jmV4F2mnBAxN_a5v430WcBJU9aDlB0OTIqmlliBRA,24215
236
+ sqlalchemy/testing/suite/test_dialect.py,sha256=D72O2WP7iinte5psWDaJXO4uRyKZNVRTK4FaxxO9fy0,24276
237
237
  sqlalchemy/testing/suite/test_insert.py,sha256=tapEYecSPKqL8xrjlcQzDOfbWdKJz9ldT7pRi0biDeM,18824
238
238
  sqlalchemy/testing/suite/test_reflection.py,sha256=Em0XdladxD6Lk9YH6ZQUw0cdEvkaXJfEPs-1E21xVn0,128866
239
239
  sqlalchemy/testing/suite/test_results.py,sha256=Aw8RK5CX1y39wDK6U8ub8PX9OYe8aoBCBCohjVPvRks,22499
@@ -243,7 +243,7 @@ sqlalchemy/testing/suite/test_sequence.py,sha256=8a6UO3pjRFnQ23ELG9m5cMMRoD3iD7a
243
243
  sqlalchemy/testing/suite/test_table_via_select.py,sha256=ZMJKUhAiqIzTKyK9J2cA_rvTij7CUt77d6o5sqDdrkc,21706
244
244
  sqlalchemy/testing/suite/test_types.py,sha256=zhL-us3ReUkX_klkbjYz0ve8op2ODSRTVLDo7OgWy38,72577
245
245
  sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=tVLf9RqCJe-CsyhCWL9d-WuWazDx5iWYzoBrSCbgjXs,6141
246
- sqlalchemy/testing/suite/test_update_delete.py,sha256=xUHytwsMyvJcH3UhzmvGpzix7yBbQ-0thmlEpF-eNI8,4008
246
+ sqlalchemy/testing/suite/test_update_delete.py,sha256=llAnxDGvbK-C7CzEXbSNEjczhOSL1sKTLuzGyJahqV8,5195
247
247
  sqlalchemy/util/__init__.py,sha256=9e3x2jLp1offzq2pWK5xIHLVQXkQtBIGDmqP9ePNS_4,8621
248
248
  sqlalchemy/util/_collections.py,sha256=kYBmJwkf3SSOmVyAPvRULLOM_3JmfXVWnxxEkEkgrUw,19157
249
249
  sqlalchemy/util/_collections_cy.pxd,sha256=DjGW5D9N6Dv_wXdIscNTGKttmjcJBCSdz5lTHtvbe2o,291
@@ -254,15 +254,15 @@ sqlalchemy/util/compat.py,sha256=9_xMEtXJsbe5IWgKbqRT9u6bdgmq47c_xXc4FtFmxcc,891
254
254
  sqlalchemy/util/concurrency.py,sha256=MpIMr-DqUnsYool27DfOeIMXndyzdWe_JnsktAYxLSc,8849
255
255
  sqlalchemy/util/cython.py,sha256=mfWXvmFqP4y3eiA2kGgcgV5GR5sTZfOob32UGY1BYA4,1968
256
256
  sqlalchemy/util/deprecations.py,sha256=9X3kYnZcVCEf1hyWU14_RNFahgUejlaLQX78B4UbhP8,12041
257
- sqlalchemy/util/langhelpers.py,sha256=K-RZycZ2NDVGsnDMyg76HLKWifon1R0P-P2GYYdCYdw,84350
257
+ sqlalchemy/util/langhelpers.py,sha256=HldAht7neroRJLZhgTFmw8wgmzVMYNIkKw7OkcwD9hw,84574
258
258
  sqlalchemy/util/preloaded.py,sha256=DMfxtGeRyWpnUN3T9NvFdkPkTBPmruWOnUjLqrw8L0M,5975
259
259
  sqlalchemy/util/queue.py,sha256=9cMV3Dea0l5tfWIHw3LUE0L3knt48gtUP_C8fBKLQeo,9740
260
260
  sqlalchemy/util/tool_support.py,sha256=iZSwvKdF5pA8UtXuwbtln6dEDgerKJWasELSi7q6Epw,6136
261
261
  sqlalchemy/util/topological.py,sha256=yMpaA2s0JYIekQF4XKwzJzy8lVo-8yeo8o1J2xzxq48,3451
262
262
  sqlalchemy/util/typing.py,sha256=p3vOTJ4F7i3eqeFw7W_tayq88U0e53ZgOsCu7eNQ3lA,21125
263
- sqlalchemy-2.1.0rc2.dist-info/licenses/AUTHORS,sha256=3B2wtdF0Va3De2k_-OQJyagPYD5EFpWCVRjDb3hslj4,492
264
- sqlalchemy-2.1.0rc2.dist-info/licenses/LICENSE,sha256=6GK7W5BPtVE8PhQojXoloS6w7MeEegqWXTEOmpVefLk,1100
265
- sqlalchemy-2.1.0rc2.dist-info/METADATA,sha256=DZu361eBzD6yhsmMA9XBMZYfNWWR6FEH_gzf99lH1iY,10912
266
- sqlalchemy-2.1.0rc2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
267
- sqlalchemy-2.1.0rc2.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
268
- sqlalchemy-2.1.0rc2.dist-info/RECORD,,
263
+ sqlalchemy-2.1.1.dist-info/licenses/AUTHORS,sha256=3B2wtdF0Va3De2k_-OQJyagPYD5EFpWCVRjDb3hslj4,492
264
+ sqlalchemy-2.1.1.dist-info/licenses/LICENSE,sha256=6GK7W5BPtVE8PhQojXoloS6w7MeEegqWXTEOmpVefLk,1100
265
+ sqlalchemy-2.1.1.dist-info/METADATA,sha256=gdC5VZ7rLaHkNA6O7U9JtIPm8CICTuMa-YwGJkLM5u0,9680
266
+ sqlalchemy-2.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
267
+ sqlalchemy-2.1.1.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
268
+ sqlalchemy-2.1.1.dist-info/RECORD,,