]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
support omission of standard event listen example
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Sep 2025 15:10:51 +0000 (11:10 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 2 Sep 2025 15:36:14 +0000 (11:36 -0400)
The required targets for before_configured() and after_configured()
are the Mapper (and soon to include registry things as well in 2.1),
update the automatic doc example thing to be able to be skipped
when there are special instructions for the target.

Also updates the event docs a bit, which were very old and
written in more of that "disorganized wall of details" style
that was very hard to unlearn

Change-Id: I729d08e25b721f4c993aa1856038b27643fc95d0

lib/sqlalchemy/event/__init__.py
lib/sqlalchemy/event/legacy.py
lib/sqlalchemy/orm/events.py

index 309b7bd33fbad36b81c6437b91855dfe48a0cdef..4d1830994322e3d3af0af9f9a035f77daa16f06e 100644 (file)
@@ -20,6 +20,7 @@ from .base import _DispatchCommon as _DispatchCommon
 from .base import dispatcher as dispatcher
 from .base import Events as Events
 from .legacy import _legacy_signature as _legacy_signature
+from .legacy import _omit_standard_example as _omit_standard_example
 from .registry import _EventKey as _EventKey
 from .registry import _ListenerFnType as _ListenerFnType
 from .registry import EventTarget as EventTarget
index e60fd9a5e17f5de1b998f8cfdc9e1a45272054ca..03037d9bb762025e97e74c7495fecde9b9ee1a1d 100644 (file)
@@ -18,6 +18,7 @@ from typing import List
 from typing import Optional
 from typing import Tuple
 from typing import Type
+from typing import TypeVar
 
 from .registry import _ET
 from .registry import _ListenerFnType
@@ -29,14 +30,16 @@ if typing.TYPE_CHECKING:
     from .base import _HasEventsDispatch
 
 
-_LegacySignatureType = Tuple[str, List[str], Optional[Callable[..., Any]]]
+_F = TypeVar("_F", bound=Callable[..., Any])
+
+_LegacySignatureType = Tuple[str, List[str], Callable[..., Any]]
 
 
 def _legacy_signature(
     since: str,
     argnames: List[str],
     converter: Optional[Callable[..., Any]] = None,
-) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
+) -> Callable[[_F], _F]:
     """legacy sig decorator
 
 
@@ -48,7 +51,7 @@ def _legacy_signature(
 
     """
 
-    def leg(fn: Callable[..., Any]) -> Callable[..., Any]:
+    def leg(fn: _F) -> _F:
         if not hasattr(fn, "_legacy_signatures"):
             fn._legacy_signatures = []  # type: ignore[attr-defined]
         fn._legacy_signatures.append((since, argnames, converter))  # type: ignore[attr-defined] # noqa: E501
@@ -57,6 +60,11 @@ def _legacy_signature(
     return leg
 
 
+def _omit_standard_example(fn: _F) -> _F:
+    fn._omit_standard_example = True  # type: ignore[attr-defined]
+    return fn
+
+
 def _wrap_fn_for_legacy(
     dispatch_collection: _ClsLevelDispatch[_ET],
     fn: _ListenerFnType,
@@ -222,6 +230,10 @@ def _augment_fn_docs(
     parent_dispatch_cls: Type[_HasEventsDispatch[_ET]],
     fn: _ListenerFnType,
 ) -> str:
+    if getattr(fn, "_omit_standard_example", False):
+        assert fn.__doc__
+        return fn.__doc__
+
     header = (
         ".. container:: event_signatures\n\n"
         "     Example argument forms::\n"
index 53429139d87b286c73e2e40005f82c90367912f7..b915cdfec887dbb9c54028fa17f5a2e6dce20cc1 100644 (file)
@@ -952,42 +952,42 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
 
         """
 
+    @event._omit_standard_example
     def before_mapper_configured(
         self, mapper: Mapper[_O], class_: Type[_O]
     ) -> None:
         """Called right before a specific mapper is to be configured.
 
-        This event is intended to allow a specific mapper to be skipped during
-        the configure step, by returning the :attr:`.orm.interfaces.EXT_SKIP`
-        symbol which indicates to the :func:`.configure_mappers` call that this
-        particular mapper (or hierarchy of mappers, if ``propagate=True`` is
-        used) should be skipped in the current configuration run. When one or
-        more mappers are skipped, the "new mappers" flag will remain set,
-        meaning the :func:`.configure_mappers` function will continue to be
-        called when mappers are used, to continue to try to configure all
-        available mappers.
-
-        In comparison to the other configure-level events,
-        :meth:`.MapperEvents.before_configured`,
-        :meth:`.MapperEvents.after_configured`, and
-        :meth:`.MapperEvents.mapper_configured`, the
-        :meth:`.MapperEvents.before_mapper_configured` event provides for a
-        meaningful return value when it is registered with the ``retval=True``
-        parameter.
-
-        e.g.::
-
+        The :meth:`.MapperEvents.before_mapper_configured` event is invoked
+        for each mapper that is encountered when the
+        :func:`_orm.configure_mappers` function proceeds through the current
+        list of not-yet-configured mappers.   It is similar to the
+        :meth:`.MapperEvents.mapper_configured` event, except that it's invoked
+        right before the configuration occurs, rather than afterwards.
+
+        The :meth:`.MapperEvents.before_mapper_configured` event includes
+        the special capability where it can force the configure step for a
+        specific mapper to be skipped; to use this feature, establish
+        the event using the ``retval=True`` parameter and return
+        the :attr:`.orm.interfaces.EXT_SKIP` symbol to indicate the mapper
+        should be left unconfigured::
+
+            from sqlalchemy import event
             from sqlalchemy.orm import EXT_SKIP
+            from sqlalchemy.orm import DeclarativeBase
 
-            Base = declarative_base()
 
-            DontConfigureBase = declarative_base()
+            class DontConfigureBase(DeclarativeBase):
+                pass
 
 
             @event.listens_for(
                 DontConfigureBase,
                 "before_mapper_configured",
+                # support return values for the event
                 retval=True,
+                # propagate the listener to all subclasses of
+                # DontConfigureBase
                 propagate=True,
             )
             def dont_configure(mapper, cls):
@@ -1032,15 +1032,14 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
         event; this event invokes only after all known mappings have been
         fully configured.
 
-        The :meth:`.MapperEvents.mapper_configured` event, unlike
+        The :meth:`.MapperEvents.mapper_configured` event, unlike the
         :meth:`.MapperEvents.before_configured` or
-        :meth:`.MapperEvents.after_configured`,
-        is called for each mapper/class individually, and the mapper is
-        passed to the event itself.  It also is called exactly once for
-        a particular mapper.  The event is therefore useful for
-        configurational steps that benefit from being invoked just once
-        on a specific mapper basis, which don't require that "backref"
-        configurations are necessarily ready yet.
+        :meth:`.MapperEvents.after_configured` events, is called for each
+        mapper/class individually, and the mapper is passed to the event
+        itself.  It also is called exactly once for a particular mapper.  The
+        event is therefore useful for configurational steps that benefit from
+        being invoked just once on a specific mapper basis, which don't require
+        that "backref" configurations are necessarily ready yet.
 
         :param mapper: the :class:`_orm.Mapper` which is the target
          of this event.
@@ -1057,6 +1056,7 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
         """
         # TODO: need coverage for this event
 
+    @event._omit_standard_example
     def before_configured(self) -> None:
         """Called before a series of mappers have been configured.
 
@@ -1068,9 +1068,15 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
         new mappers have been made available and new mapper use is
         detected.
 
+        Similar events to this one include
+        :meth:`.MapperEvents.after_configured`, which is invoked after a series
+        of mappers has been configured, as well as
+        :meth:`.MapperEvents.before_mapper_configured` and
+        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a
+        per-mapper basis.
+
         This event can **only** be applied to the :class:`_orm.Mapper` class,
-        and not to individual mappings or mapped classes. It is only invoked
-        for all mappings as a whole::
+        and not to individual mappings or mapped classes::
 
             from sqlalchemy.orm import Mapper
 
@@ -1078,25 +1084,11 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
             @event.listens_for(Mapper, "before_configured")
             def go(): ...
 
-        Contrast this event to :meth:`.MapperEvents.after_configured`,
-        which is invoked after the series of mappers has been configured,
-        as well as :meth:`.MapperEvents.before_mapper_configured`
-        and :meth:`.MapperEvents.mapper_configured`, which are both invoked
-        on a per-mapper basis.
-
-        Theoretically this event is called once per
-        application, but is actually called any time new mappers
-        are to be affected by a :func:`_orm.configure_mappers`
-        call.   If new mappings are constructed after existing ones have
-        already been used, this event will likely be called again.  To ensure
-        that a particular event is only called once and no further, the
-        ``once=True`` argument (new in 0.9.4) can be applied::
-
-            from sqlalchemy.orm import mapper
-
-
-            @event.listens_for(mapper, "before_configured", once=True)
-            def go(): ...
+        Typically, this event is called once per application, but in practice
+        may be called more than once, any time new mappers are to be affected
+        by a :func:`_orm.configure_mappers` call.   If new mappings are
+        constructed after existing ones have already been used, this event will
+        likely be called again.
 
         .. seealso::
 
@@ -1108,6 +1100,7 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
 
         """
 
+    @event._omit_standard_example
     def after_configured(self) -> None:
         """Called after a series of mappers have been configured.
 
@@ -1119,17 +1112,15 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
         new mappers have been made available and new mapper use is
         detected.
 
-        Contrast this event to the :meth:`.MapperEvents.mapper_configured`
-        event, which is called on a per-mapper basis while the configuration
-        operation proceeds; unlike that event, when this event is invoked,
-        all cross-configurations (e.g. backrefs) will also have been made
-        available for any mappers that were pending.
-        Also contrast to :meth:`.MapperEvents.before_configured`,
-        which is invoked before the series of mappers has been configured.
+        Similar events to this one include
+        :meth:`.MapperEvents.before_configured`, which is invoked before a
+        series of mappers are configured, as well as
+        :meth:`.MapperEvents.before_mapper_configured` and
+        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a
+        per-mapper basis.
 
         This event can **only** be applied to the :class:`_orm.Mapper` class,
-        and not to individual mappings or
-        mapped classes.  It is only invoked for all mappings as a whole::
+        and not to individual mappings or mapped classes::
 
             from sqlalchemy.orm import Mapper
 
@@ -1137,19 +1128,11 @@ class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
             @event.listens_for(Mapper, "after_configured")
             def go(): ...
 
-        Theoretically this event is called once per
-        application, but is actually called any time new mappers
-        have been affected by a :func:`_orm.configure_mappers`
-        call.   If new mappings are constructed after existing ones have
-        already been used, this event will likely be called again.  To ensure
-        that a particular event is only called once and no further, the
-        ``once=True`` argument (new in 0.9.4) can be applied::
-
-            from sqlalchemy.orm import mapper
-
-
-            @event.listens_for(mapper, "after_configured", once=True)
-            def go(): ...
+        Typically, this event is called once per application, but in practice
+        may be called more than once, any time new mappers are to be affected
+        by a :func:`_orm.configure_mappers` call.   If new mappings are
+        constructed after existing ones have already been used, this event will
+        likely be called again.
 
         .. seealso::