diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-03-30 14:48:39 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-03-30 17:12:48 -0400 |
| commit | 8eaf6f30cb8b316a6a6b4bb8743322f03b4903c4 (patch) | |
| tree | 06214da20a99ce89b0b2a0f31464f06ba336a8fa /test/orm | |
| parent | 72d2ec57928bdf5649c551bdaa87b7fb0943c2fe (diff) | |
| download | sqlalchemy-8eaf6f30cb8b316a6a6b4bb8743322f03b4903c4.tar.gz | |
warn for DC mixin / abstract fields that are not on a dataclass
Fields that are declared on Declarative Mixins and then combined with
classes that make use of :class:`_orm.MappedAsDataclass`, where those mixin
fields are not themselves part of a dataclass, now emit a deprecation
warning as these fields will be ignored in a future release, as Python
dataclasses behavior is to ignore these fields. Type checkers will not see
these fields under pep-681.
Fixes: #9350
Change-Id: Ie0a0ac0f0bb58d1c2aae13b4a8dcd0439a4f5477
Diffstat (limited to 'test/orm')
| -rw-r--r-- | test/orm/declarative/test_dc_transforms.py | 122 |
1 files changed, 88 insertions, 34 deletions
diff --git a/test/orm/declarative/test_dc_transforms.py b/test/orm/declarative/test_dc_transforms.py index 5abcaa46e..a8a5e04bb 100644 --- a/test/orm/declarative/test_dc_transforms.py +++ b/test/orm/declarative/test_dc_transforms.py @@ -1,5 +1,7 @@ +import contextlib import dataclasses from dataclasses import InitVar +import functools import inspect as pyinspect from itertools import product from typing import Any @@ -56,6 +58,13 @@ from sqlalchemy.testing import Variation from sqlalchemy.util import compat +def _dataclass_mixin_warning(clsname, attrnames): + return testing.expect_deprecated( + rf"When transforming .* to a dataclass, attribute\(s\) " + rf"{attrnames} originates from superclass .*{clsname}" + ) + + class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): @testing.fixture(params=["(MAD, DB)", "(DB, MAD)"]) def dc_decl_base(self, request, metadata): @@ -197,6 +206,32 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): a1 = Address("email@address") eq_(a1.email_address, "email@address") + def test_warn_on_non_dc_mixin(self): + class _BaseMixin: + + create_user: Mapped[int] = mapped_column() + update_user: Mapped[Optional[int]] = mapped_column( + default=None, init=False + ) + + class Base(DeclarativeBase, MappedAsDataclass, _BaseMixin): + pass + + class SubMixin: + foo: Mapped[str] + bar: Mapped[str] = mapped_column() + + with _dataclass_mixin_warning( + "_BaseMixin", "'create_user', 'update_user'" + ), _dataclass_mixin_warning("SubMixin", "'foo', 'bar'"): + + class User(SubMixin, Base): + __tablename__ = "sys_user" + + id: Mapped[int] = mapped_column(primary_key=True, init=False) + username: Mapped[str] = mapped_column(String) + password: Mapped[str] = mapped_column(String) + def test_basic_constructor_repr_cls_decorator( self, registry: _RegistryType ): @@ -667,10 +702,10 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase): def test_mapped_column_overrides(self, dc_decl_base): """test #8688""" - class TriggeringMixin: + class TriggeringMixin(MappedAsDataclass): mixin_value: Mapped[int] = mapped_column(BigInteger) - class NonTriggeringMixin: + class NonTriggeringMixin(MappedAsDataclass): mixin_value: Mapped[int] class Foo(dc_decl_base, TriggeringMixin): @@ -1167,6 +1202,9 @@ class DataclassesForNonMappedClassesTest(fixtures.TestBase): ) expected_annotations[Mixin] = {} + + non_dc_mixin = contextlib.nullcontext + else: class Mixin: @@ -1204,50 +1242,66 @@ class DataclassesForNonMappedClassesTest(fixtures.TestBase): insert_default=cls.__name__, ) - if dataclass_scope.on_base_class: + non_dc_mixin = functools.partial( + _dataclass_mixin_warning, "Mixin", "'polymorphic_type'" + ) - class Book(Mixin, MappedAsDataclass, Base, **klass_kw): - id: Mapped[int] = mapped_column( - Integer, - primary_key=True, - init=False, - ) + if dataclass_scope.on_base_class: - else: + with non_dc_mixin(): - class Book(Mixin, Base): - if not dataclass_scope.on_sub_class: - id: Mapped[int] = mapped_column( # noqa: A001 - Integer, primary_key=True, init=False - ) - else: - id: Mapped[int] = mapped_column( # noqa: A001 + class Book(Mixin, MappedAsDataclass, Base, **klass_kw): + id: Mapped[int] = mapped_column( Integer, primary_key=True, + init=False, ) + else: + if dataclass_scope.on_base: + local_non_dc_mixin = non_dc_mixin + else: + local_non_dc_mixin = contextlib.nullcontext + + with local_non_dc_mixin(): + + class Book(Mixin, Base): + if not dataclass_scope.on_sub_class: + id: Mapped[int] = mapped_column( # noqa: A001 + Integer, primary_key=True, init=False + ) + else: + id: Mapped[int] = mapped_column( # noqa: A001 + Integer, + primary_key=True, + ) + if MappedAsDataclass in Book.__mro__: expected_annotations[Book] = {"id": int, "polymorphic_type": str} if dataclass_scope.on_sub_class: - class Novel(MappedAsDataclass, Book, **klass_kw): - id: Mapped[int] = mapped_column( # noqa: A001 - ForeignKey("book.id"), - primary_key=True, - init=False, - ) - description: Mapped[Optional[str]] + with non_dc_mixin(): + + class Novel(MappedAsDataclass, Book, **klass_kw): + id: Mapped[int] = mapped_column( # noqa: A001 + ForeignKey("book.id"), + primary_key=True, + init=False, + ) + description: Mapped[Optional[str]] else: - class Novel(Book): - id: Mapped[int] = mapped_column( - ForeignKey("book.id"), - primary_key=True, - init=False, - ) - description: Mapped[Optional[str]] + with non_dc_mixin(): + + class Novel(Book): + id: Mapped[int] = mapped_column( + ForeignKey("book.id"), + primary_key=True, + init=False, + ) + description: Mapped[Optional[str]] expected_annotations[Novel] = {"id": int, "description": Optional[str]} @@ -1712,7 +1766,7 @@ class MixinColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): class BaseEntity(DeclarativeBase): pass - class IdMixin: + class IdMixin(MappedAsDataclass): id: Mapped[int] = mapped_column( primary_key=True, init=False ) @@ -1764,14 +1818,14 @@ class MixinColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL): elif mad_setup == "subclass": - class BaseEntity(DeclarativeBase): + class BaseEntity(MappedAsDataclass, DeclarativeBase): id: Mapped[int] = mapped_column( primary_key=True, init=False ) if mad_setup == "subclass": - class A(MappedAsDataclass, BaseEntity, **dataclass_kw): + class A(BaseEntity, **dataclass_kw): __mapper_args__ = { "polymorphic_on": "type", "polymorphic_identity": "a", |
