From: Mike Bayer Date: Mon, 6 Feb 2023 15:16:26 +0000 (-0500) Subject: check for superclasses of user defined init X-Git-Tag: rel_2_0_2~7^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b927d95d075c4cdf027f784e759c9904416fb73a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git check for superclasses of user defined init Fixed regression caused by the fix for :ticket:`9171`, which itself was fixing a regression, involving the mechanics of ``__init__()`` on classes that extend from :class:`_orm.DeclarativeBase`. The change made it such that ``__init__()`` was applied to the user-defined base if there were no ``__init__()`` method directly on the class. This has been adjusted so that ``__init__()`` is applied only if no other class in the hierarchy of the user-defined base has an ``__init__()`` method. This again allows user-defined base classes based on :class:`_orm.DeclarativeBase` to include mixins that themselves include a custom ``__init__()`` method. Fixes: #9249 Change-Id: I78f32590ce9ffe245eccb4bd5bd7c884d4e015d5 --- diff --git a/doc/build/changelog/unreleased_20/9249.rst b/doc/build/changelog/unreleased_20/9249.rst new file mode 100644 index 0000000000..1ffd63a012 --- /dev/null +++ b/doc/build/changelog/unreleased_20/9249.rst @@ -0,0 +1,13 @@ +.. change:: + :tags: bug, orm + :tickets: 9249 + + Fixed regression caused by the fix for :ticket:`9171`, which itself was + fixing a regression, involving the mechanics of ``__init__()`` on classes + that extend from :class:`_orm.DeclarativeBase`. The change made it such + that ``__init__()`` was applied to the user-defined base if there were no + ``__init__()`` method directly on the class. This has been adjusted so that + ``__init__()`` is applied only if no other class in the hierarchy of the + user-defined base has an ``__init__()`` method. This again allows + user-defined base classes based on :class:`_orm.DeclarativeBase` to include + mixins that themselves include a custom ``__init__()`` method. diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py index e3e2611da1..5f2b3c6966 100644 --- a/lib/sqlalchemy/orm/decl_api.py +++ b/lib/sqlalchemy/orm/decl_api.py @@ -567,7 +567,7 @@ def _setup_declarative_base(cls: Type[Any]) -> None: if "metadata" not in cls.__dict__: cls.metadata = cls.registry.metadata # type: ignore - if "__init__" not in cls.__dict__: + if getattr(cls, "__init__", object.__init__) is object.__init__: cls.__init__ = cls.registry.constructor diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py index 45f0d4200b..d4890a7815 100644 --- a/test/orm/declarative/test_basic.py +++ b/test/orm/declarative/test_basic.py @@ -134,7 +134,13 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase): @testing.variation( "base_type", - ["declbase", "declbasenometa", "declbasefn", "asdeclarative"], + [ + "declbase", + "declbasenometa", + "declbasefn", + "asdeclarative", + "mixinonbase", + ], ) def test_reg_constructor_custom_init(self, base_type): """test for #9171 testing what an explicit __init__ does. @@ -165,6 +171,15 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase): m1.init(x) Base = declarative_base(cls=_B) + elif base_type.mixinonbase: + + class Mixin: + def __init__(self, x=None): + m1.init(x) + + class Base(Mixin, DeclarativeBase): + pass + elif base_type.asdeclarative: @as_declarative() @@ -180,7 +195,11 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase): fs = fakeself() - if base_type.declbase or base_type.declbasenometa: + if ( + base_type.declbase + or base_type.declbasenometa + or base_type.mixinonbase + ): Base.__init__(fs, x=5) eq_(m1.mock_calls, [mock.call.init(5)]) else: