summaryrefslogtreecommitdiff
path: root/test/orm
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2023-03-30 14:48:39 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2023-03-30 17:12:48 -0400
commit8eaf6f30cb8b316a6a6b4bb8743322f03b4903c4 (patch)
tree06214da20a99ce89b0b2a0f31464f06ba336a8fa /test/orm
parent72d2ec57928bdf5649c551bdaa87b7fb0943c2fe (diff)
downloadsqlalchemy-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.py122
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",