Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/diffusers/models/modeling_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,10 @@ def set_attention_backend(self, backend: str) -> None:
continue
processor._attention_backend = backend

# Important to set the active backend so that it propagates gracefully throughout.
_AttentionBackendRegistry.set_active_backend(backend)
# Only pin the backend on this model's attention modules.
# Do not flip the process-wide active backend: that would leak into
# other models whose per-module backend is still unset (see #14249).
# Use `attention_backend(...)` when a temporary global override is needed.

def reset_attention_backend(self) -> None:
"""
Expand Down
44 changes: 44 additions & 0 deletions tests/models/test_attention_backend_isolation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# coding=utf-8
# Copyright 2026 The HuggingFace Team.
#
# Licensed under the Apache License, Version 2.0.

"""Unit tests for attention backend process isolation."""

from diffusers.models.attention_dispatch import (
AttentionBackendName,
_AttentionBackendRegistry,
)
from diffusers.models.modeling_utils import ModelMixin
from diffusers.models.attention_processor import Attention


class _TinyAttentionModel(ModelMixin):
def __init__(self):
super().__init__()
# Minimal Attention module so set_attention_backend has a processor to stamp.
self.attn = Attention(query_dim=16, heads=2, dim_head=8)


def test_set_attention_backend_does_not_mutate_process_global_registry():
"""model.set_attention_backend must not leak into the process-global registry."""
initial_backend, _ = _AttentionBackendRegistry.get_active_backend()
model = _TinyAttentionModel()

try:
# Pick a backend different from the current global default when possible.
target = AttentionBackendName.NATIVE
if initial_backend == target:
# Still exercise the API; isolation is what we assert.
pass
model.set_attention_backend(target.value)
active_backend, _ = _AttentionBackendRegistry.get_active_backend()
assert active_backend == initial_backend, (
"set_attention_backend must not change the process-global active backend; "
f"expected {initial_backend}, got {active_backend}"
)
# Per-module processor should still be stamped.
assert model.attn.processor._attention_backend == target
finally:
model.reset_attention_backend()
_AttentionBackendRegistry.set_active_backend(initial_backend)
Loading