diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-05-09 11:19:43 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-05-09 16:39:31 -0400 |
commit | 8ee129d988c2499766b1f09c5e21383b88dcc204 (patch) | |
tree | a34347c0bf1b0d5ef21e13eebb46543509ef27be /lib/sqlalchemy/ext/mutable.py | |
parent | 946e71efdfc93777027f4fd7360a524051be393d (diff) | |
download | sqlalchemy-8ee129d988c2499766b1f09c5e21383b88dcc204.tar.gz |
guard against duplicate mutable event listeners
Fixed issue in :class:`_mutable.Mutable` where event registration for ORM
mapped attributes would be called repeatedly for mapped inheritance
subclasses, leading to duplicate events being invoked in inheritance
hierarchies.
Fixes: #9676
Change-Id: I91289141d7a5f5c86a9033596735ed6eba7071b0
Diffstat (limited to 'lib/sqlalchemy/ext/mutable.py')
-rw-r--r-- | lib/sqlalchemy/ext/mutable.py | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/lib/sqlalchemy/ext/mutable.py b/lib/sqlalchemy/ext/mutable.py index 7d23f9fda..0f82518aa 100644 --- a/lib/sqlalchemy/ext/mutable.py +++ b/lib/sqlalchemy/ext/mutable.py @@ -693,14 +693,28 @@ class Mutable(MutableBase): ) -> None: if mapper.non_primary: return + _APPLIED_KEY = "_ext_mutable_listener_applied" + for prop in mapper.column_attrs: if ( - schema_event_check - and hasattr(prop.expression, "info") - and prop.expression.info.get("_ext_mutable_orig_type") # type: ignore # noqa: E501 # TODO: https://github.com/python/mypy/issues/1424#issuecomment-1272354487 - is sqltype - ) or (prop.columns[0].type is sqltype): - cls.associate_with_attribute(getattr(class_, prop.key)) + # all Mutable types refer to a Column that's mapped, + # since this is the only kind of Core target the ORM can + # "mutate" + isinstance(prop.expression, Column) + and ( + ( + schema_event_check + and prop.expression.info.get( + "_ext_mutable_orig_type" + ) + is sqltype + ) + or prop.expression.type is sqltype + ) + ): + if not prop.expression.info.get(_APPLIED_KEY, False): + prop.expression.info[_APPLIED_KEY] = True + cls.associate_with_attribute(getattr(class_, prop.key)) event.listen(Mapper, "mapper_configured", listen_for_type) @@ -724,7 +738,6 @@ class MutableComposite(MutableBase): """Subclasses should call this method whenever change events occur.""" for parent, key in self._parents.items(): - prop = parent.mapper.get_property(key) for value, attr_name in zip( prop._composite_values_from_instance(self), @@ -781,7 +794,6 @@ class MutableDict(Mutable, Dict[_KT, _VT]): self.changed() if TYPE_CHECKING: - # from https://github.com/python/mypy/issues/14858 @overload |