From: w-Jessamine <148705640+w-Jessamine@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:30:16 +0000 (-0400) Subject: Allow inspection registrations from module reloads X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0c6e2f39df1e6748a916fc5f5a1a3b3296aed4af;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Allow inspection registrations from module reloads Allowed the inspection registry to replace an existing registration with a reloaded callable from the same module and name. This avoids an assertion failure for tooling that unloads and reloads SQLAlchemy modules while still rejecting conflicting registrations. Pull request courtersy w-Jessamine. Fixes: #10748 Closes: #13402 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/13402 Pull-request-sha: e54815ef0092da05277efab79b90b77e44734cb9 Change-Id: If2565206d6b861993df8c361511551e204120b5e --- diff --git a/doc/build/changelog/unreleased_21/10748.rst b/doc/build/changelog/unreleased_21/10748.rst new file mode 100644 index 0000000000..b27d8d413c --- /dev/null +++ b/doc/build/changelog/unreleased_21/10748.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, general + :tickets: 10748 + + Allowed the inspection registry to replace an existing registration with a + reloaded callable from the same module and name. This avoids an assertion + failure for tooling that unloads and reloads SQLAlchemy modules while still + rejecting conflicting registrations. Pull request courtersy w-Jessamine. diff --git a/lib/sqlalchemy/inspection.py b/lib/sqlalchemy/inspection.py index 2758a527bd..5187483896 100644 --- a/lib/sqlalchemy/inspection.py +++ b/lib/sqlalchemy/inspection.py @@ -158,7 +158,20 @@ def _inspects( def decorate(fn_or_cls: _F) -> _F: for type_ in types: if type_ in _registrars: - raise AssertionError("Type %s is already registered" % type_) + existing = _registrars[type_] + existing_name = getattr(existing, "__name__", None) + existing_module = getattr(existing, "__module__", None) + + # allow a new version of the same function or class, that is, + # a module reload; compare name/module attributes because + # classes may define custom __eq__ behavior. + if ( + existing_name != fn_or_cls.__name__ + or existing_module != fn_or_cls.__module__ + ): + raise AssertionError( + "Type %s is already registered" % type_ + ) _registrars[type_] = fn_or_cls return fn_or_cls diff --git a/test/base/test_inspect.py b/test/base/test_inspect.py index fd433ce99a..cd90f9f308 100644 --- a/test/base/test_inspect.py +++ b/test/base/test_inspect.py @@ -74,3 +74,36 @@ class TestInspection(fixtures.TestBase): SomeFoo() eq_(inspect(SomeFoo()), 1) eq_(inspect(SomeSubFoo()), 2) + + def test_inspects_allows_reloaded_registrar(self): + """test #10748""" + + class SomeFoo(TestFixture): + pass + + @inspection._inspects(SomeFoo) + def insp_somefoo(subject): + return 1 + + reloaded_insp_somefoo = type(insp_somefoo)( + insp_somefoo.__code__, + insp_somefoo.__globals__, + insp_somefoo.__name__, + insp_somefoo.__defaults__, + insp_somefoo.__closure__, + ) + reloaded_insp_somefoo.__kwdefaults__ = insp_somefoo.__kwdefaults__ + + def insp_other_somefoo(subject): + return 2 + + assert_raises_message( + AssertionError, + "Type .* is already registered", + inspection._inspects(SomeFoo), + insp_other_somefoo, + ) + + inspection._inspects(SomeFoo)(reloaded_insp_somefoo) + assert inspection._registrars[SomeFoo] is reloaded_insp_somefoo + eq_(inspect(SomeFoo()), 1)