Hook bridges and override resolution

bridge_base and bridges_to are private aiodrf implementation helpers. They preserve synchronous DRF extension contracts while exposing awaitable hooks. They are not Django decorators or application configuration APIs.

Registration of framework defaults

bridge_base(cls) registers the exact class in utils._BRIDGE_BASES, a weak set, and returns the same class. It does not replace methods, modify bases or patch DRF. Registration invalidates pair-resolution metadata that may have previously treated the class as an application override.

Identity matters: registering a base does not register all its subclasses. A subclass implementing a hook remains custom. Framework modules register their defaults at import time; this is not a request-time or concurrent configuration API. Weak registrations do not retain dynamically constructed classes.

The loops registering DRF serializer and mixin bases identify existing defaults; they do not alter those classes. Deleting the loop variable only removes a temporary module name.

Selecting a hook

resolve_pair(obj, sync_name, async_name) decides which implementation wins. Execution context is a separate decision.

Definition Selection
Async member on the instance Async member
Only sync member on the instance Sync member, or coroutine under the sync name
Two custom class members Closest defining class in the MRO; async wins a tie
Only one custom class member That member
Only registered defaults Framework default

Pass the instance when one exists; inspecting only its type would miss an instance-level override. Slotted objects without an instance dictionary fall back to class inspection. The class-only decision is weakly cached; changing class definitions during active requests is not a supported invalidation model.

This precedence is security-relevant. For example, accidentally selecting BasePermission.has_permission() instead of an application's async denial would allow access. The bridge tests cover overrides and inherited defaults, not just successful view execution.

Synchronous callers

bridges_to("ahas_permission") decorates the synchronous method on a framework base. When the async override wins, the wrapper invokes it through async_to_sync(invoke); otherwise the original synchronous body runs. This supports DRF code such as synchronous extension hooks, schema tooling and the browsable API.

A bridge must run from synchronous code or a worker thread. It cannot block the event-loop thread waiting for that same loop; async callers must await the async operation. call_pair_sync additionally handles coroutine callables stored under a synchronous name and synchronous wrappers returning awaitables.

invoke awaits known async callables. An unknown synchronous wrapper executes in the thread-sensitive worker, then its returned awaitable is awaited on the loop. A wrapper's __wrapped__ metadata does not prove its synchronous prefix is nonblocking.

Serializer fallback and recursion

Async serializer defaults sometimes need DRF's original synchronous operation. Calling the ordinary bound method could re-enter the reverse bridge indefinitely. aio._common._sync_member therefore skips bases marked _async_serializer_bridge. When async wins, it also skips custom sync methods that lost the pair decision; when an instance sync override wins, it returns that callable.

_bridged(cls, sync_name) checks both the marker and the requested method's presence. An unrelated marked base must not satisfy a save or validation guard. The marker identifies a fallback boundary; exact bridge-base registration identifies framework defaults. Neither marker is a purity or thread-safety claim.

State and cancellation

Class metadata caches have weak keys and capacity bounds. Computation occurs outside publication locks; invalidation rejects stale publication. See state ownership for cache lifetimes and retained values.

If a synchronous wrapper returns a native coroutine after its awaiting caller has been cancelled, run_sync_and_await closes that coroutine. It cannot stop an already running synchronous operation or roll back its side effects. Applications still own their tasks, transactions and external resources.

Source

These helpers are private. Extend applications through the documented hooks; do not register arbitrary classes as defaults to bypass execution checks.