From fbafc24d21cb8f791e72daa28fb26557a41a8d30 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 12 Sep 2023 08:01:54 -0400 Subject: [PATCH] allow any key for naming_convention dict, typing is not possible Fixed regression introduced in 2.0.20 via :ticket:`9600` fix which attempted to add more formal typing to :paramref:`_schema.MetaData.naming_convention`. This change prevented basic naming convention dictionaries from passing typing and has been adjusted so that a plain dictionary of strings for keys as well as dictionaries that use constraint types as keys or a mix of both, are again accepted. As part of this change, lesser used forms of the naming convention dictionary are also typed, including that it currently allows for ``Constraint`` type objects as keys as well. Fixes: #9284 Fixes: #10264 Change-Id: Ic6561dd65058e4de3a7a393295b9863fc065db13 --- doc/build/changelog/unreleased_20/10264.rst | 14 +++++++++ lib/sqlalchemy/sql/schema.py | 31 ++++++++---------- test/sql/test_metadata.py | 9 ++++++ test/typing/plain_files/sql/schema.py | 35 +++++++++++++++++++++ 4 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 doc/build/changelog/unreleased_20/10264.rst diff --git a/doc/build/changelog/unreleased_20/10264.rst b/doc/build/changelog/unreleased_20/10264.rst new file mode 100644 index 0000000000..bdfd5b92c2 --- /dev/null +++ b/doc/build/changelog/unreleased_20/10264.rst @@ -0,0 +1,14 @@ +.. change:: + :tags: bug, typing + :tickets: 10264, 9284 + + Fixed regression introduced in 2.0.20 via :ticket:`9600` fix which + attempted to add more formal typing to + :paramref:`_schema.MetaData.naming_convention`. This change prevented basic + naming convention dictionaries from passing typing and has been adjusted so + that a plain dictionary of strings for keys as well as dictionaries that + use constraint types as keys or a mix of both, are again accepted. + + As part of this change, lesser used forms of the naming convention + dictionary are also typed, including that it currently allows for + ``Constraint`` type objects as keys as well. diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 008ae2c005..8baf2de6a4 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -50,7 +50,6 @@ from typing import overload from typing import Sequence as _typing_Sequence from typing import Set from typing import Tuple -from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union @@ -5286,30 +5285,26 @@ class Index( ) -_AllConstraints = Union[ - Index, - UniqueConstraint, - CheckConstraint, - ForeignKeyConstraint, - PrimaryKeyConstraint, -] - -_NamingSchemaCallable = Callable[[_AllConstraints, Table], str] +_NamingSchemaCallable = Callable[[Constraint, Table], str] +_NamingSchemaDirective = Union[str, _NamingSchemaCallable] class _NamingSchemaTD(TypedDict, total=False): - fk: Union[str, _NamingSchemaCallable] - pk: Union[str, _NamingSchemaCallable] - ix: Union[str, _NamingSchemaCallable] - ck: Union[str, _NamingSchemaCallable] - uq: Union[str, _NamingSchemaCallable] + fk: _NamingSchemaDirective + pk: _NamingSchemaDirective + ix: _NamingSchemaDirective + ck: _NamingSchemaDirective + uq: _NamingSchemaDirective _NamingSchemaParameter = Union[ + # it seems like the TypedDict here is useful for pylance typeahead, + # and not much else _NamingSchemaTD, - Mapping[ - Union[Type[_AllConstraints], str], Union[str, _NamingSchemaCallable] - ], + # there is no form that allows Union[Type[Any], str] to work in all + # cases, including breaking out Mapping[] entries for each combination + # even, therefore keys must be `Any` (see #10264) + Mapping[Any, _NamingSchemaDirective], ] diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py index dce5b9984f..659409de25 100644 --- a/test/sql/test_metadata.py +++ b/test/sql/test_metadata.py @@ -5447,6 +5447,15 @@ class NamingConventionTest(fixtures.TestBase, AssertsCompiledSQL): "CONSTRAINT %s PRIMARY KEY (x, y))" % expected_name, ) + def test_constraint_classes_for_keys(self): + u1 = self._fixture( + naming_convention={ + UniqueConstraint: "uq_%(table_name)s_%(column_0_name)s" + } + ) + uq = UniqueConstraint(u1.c.data) + eq_(uq.name, "uq_user_data") + def test_uq_name(self): u1 = self._fixture( naming_convention={"uq": "uq_%(table_name)s_%(column_0_name)s"} diff --git a/test/typing/plain_files/sql/schema.py b/test/typing/plain_files/sql/schema.py index 1e0a1345ae..7e5f054744 100644 --- a/test/typing/plain_files/sql/schema.py +++ b/test/typing/plain_files/sql/schema.py @@ -1,5 +1,6 @@ from typing import Union +from sqlalchemy import CheckConstraint from sqlalchemy import Constraint from sqlalchemy import Index from sqlalchemy import MetaData @@ -31,3 +32,37 @@ MetaData( "foo": lambda c, t: t.name + str(c.name), } ) + +NAMING_CONVENTIONS_ONLY_CALLABLE = { + "fk_guid": fk_guid, + "foo": lambda c, t: t.name + str(c.name), +} + +MetaData(naming_convention=NAMING_CONVENTIONS_ONLY_CALLABLE) + +NAMING_CONVENTIONS_TYPES_FOR_KEYS_ONLY = { + CheckConstraint: "%(table_name)s_%(constraint_name)s_ck", + Index: "%(column_0_label)s_ix", +} + +MetaData(naming_convention=NAMING_CONVENTIONS_TYPES_FOR_KEYS_ONLY) + +NAMING_CONVENTIONS_TYPES_AND_STR_FOR_KEYS = { + CheckConstraint: "%(table_name)s_%(constraint_name)s_ck", + Index: "%(column_0_label)s_ix", + "custom": "custom", + "fk": "fk_name", +} + +MetaData(naming_convention=NAMING_CONVENTIONS_TYPES_AND_STR_FOR_KEYS) + + +NAMING_CONVENTIONS_STR = { + "ix": "%(column_0_label)s_ix", + "uq": "%(table_name)s_%(column_0_name)s_uq", + "ck": "%(table_name)s_%(constraint_name)s_ck", + "fk": "%(table_name)s_%(column_0_name)s_%(referred_table_name)s_fk", + "pk": "%(table_name)s_pk", +} + +MetaData(naming_convention=NAMING_CONVENTIONS_STR) -- 2.47.3