summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2023-01-28 14:37:33 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2023-01-28 14:47:32 -0500
commit960f6dcefe24c6894844366ef6fff60fbcf1ccd6 (patch)
treee7e00e7745618e0abff900e8855dfb6203681f26 /test
parent586df197615d91af56aefc0d5ff94ceac13154eb (diff)
downloadsqlalchemy-960f6dcefe24c6894844366ef6fff60fbcf1ccd6.tar.gz
add __init__ to DeclarativeBase directly
Fixed regression in :class:`.DeclarativeBase` class where the registry's default constructor would not be applied to the base itself, which is different from how the previous :func:`_orm.declarative_base` construct works. This would prevent a mapped class with its own ``__init__()`` method from calling ``super().__init__()`` in order to access the registry's default constructor and automatically populate attributes, instead hitting ``object.__init__()`` which would raise a ``TypeError`` on any arguments. This is a very simple change in code, however explaining it is very complicated. Fixes: #9171 Change-Id: I4baecdf671861a8198d835e286fe19a51ecda126
Diffstat (limited to 'test')
-rw-r--r--test/orm/declarative/test_basic.py99
1 files changed, 99 insertions, 0 deletions
diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py
index 28fdc97f2..e2108f888 100644
--- a/test/orm/declarative/test_basic.py
+++ b/test/orm/declarative/test_basic.py
@@ -14,6 +14,7 @@ from sqlalchemy import String
from sqlalchemy import testing
from sqlalchemy import UniqueConstraint
from sqlalchemy.ext.hybrid import hybrid_property
+from sqlalchemy.orm import as_declarative
from sqlalchemy.orm import backref
from sqlalchemy.orm import class_mapper
from sqlalchemy.orm import clear_mappers
@@ -89,6 +90,104 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase):
with testing.expect_raises(exc.UnboundExecutionError):
s.get_bind(User)
+ @testing.variation(
+ "base_type",
+ ["declbase", "declbasenometa", "declbasefn", "asdeclarative"],
+ )
+ def test_reg_constructor_is_present(self, base_type):
+ """test #9171"""
+
+ if base_type.declbase:
+
+ class Base(DeclarativeBase):
+ pass
+
+ elif base_type.declbasenometa:
+
+ class Base(DeclarativeBaseNoMeta):
+ pass
+
+ elif base_type.declbasefn:
+ Base = declarative_base()
+ elif base_type.asdeclarative:
+
+ @as_declarative()
+ class Base:
+ pass
+
+ else:
+ base_type.fail()
+
+ # check for direct assignment
+ is_(Base.registry.constructor, Base.__init__)
+ is_(Base.__dict__["__init__"], Base.__init__)
+
+ class fakeself:
+ foo = None
+ bar = None
+
+ fs = fakeself()
+ Base.__init__(fs, foo="bar", bar="bat")
+ eq_(fs.foo, "bar")
+ eq_(fs.bar, "bat")
+
+ @testing.variation(
+ "base_type",
+ ["declbase", "declbasenometa", "declbasefn", "asdeclarative"],
+ )
+ def test_reg_constructor_custom_init(self, base_type):
+ """test for #9171 testing what an explicit __init__ does.
+
+ Here we decide that newer DeclarativeBase superclasses should
+ honor the ``__init__`` that's given.
+
+ """
+
+ m1 = mock.Mock()
+
+ if base_type.declbase:
+
+ class Base(DeclarativeBase):
+ def __init__(self, x=None):
+ m1.init(x)
+
+ elif base_type.declbasenometa:
+
+ class Base(DeclarativeBaseNoMeta):
+ def __init__(self, x=None):
+ m1.init(x)
+
+ elif base_type.declbasefn:
+
+ class _B:
+ def __init__(self, x=None):
+ m1.init(x)
+
+ Base = declarative_base(cls=_B)
+ elif base_type.asdeclarative:
+
+ @as_declarative()
+ class Base:
+ def __init__(self, x=None):
+ m1.init(x)
+
+ else:
+ base_type.fail()
+
+ class fakeself:
+ pass
+
+ fs = fakeself()
+
+ if base_type.declbase or base_type.declbasenometa:
+ Base.__init__(fs, x=5)
+ eq_(m1.mock_calls, [mock.call.init(5)])
+ else:
+ with expect_raises_message(
+ TypeError, "'x' is an invalid keyword argument for fakeself"
+ ):
+ Base.__init__(fs, x=5)
+
def test_dispose_attrs(self):
reg = registry()