mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
perf(api): avoid lazy module lookups in Redis test fixtures
This commit is contained in:
1 parent
af7f6abb95
commit
0d1bd869ad
2 files changed
+57
-8
No files matched your search
@@ -2,9 +2,11 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from collections.abc import Callable, Iterator
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from types import ModuleType
|
||||
from typing import TYPE_CHECKING
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -53,16 +55,20 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
def _patch_redis_clients_on_loaded_modules() -> None:
|
||||
"""Ensure any module-level redis_client references point to the shared redis_mock."""
|
||||
"""Rebind concrete module globals without invoking module-level ``__getattr__``.
|
||||
|
||||
import sys
|
||||
|
||||
for module in list(sys.modules.values()):
|
||||
if module is None:
|
||||
Inspect each time so late imports, reloads, and newly added globals are still
|
||||
covered. Attribute lookup on thousands of unrelated lazy modules is costly
|
||||
and can itself trigger imports; only stored references need rebinding.
|
||||
"""
|
||||
for module in sys.modules.copy().values():
|
||||
if not isinstance(module, ModuleType):
|
||||
continue
|
||||
for client_attribute in ("redis_client", "_pubsub_redis_client"):
|
||||
if hasattr(module, client_attribute):
|
||||
setattr(module, client_attribute, redis_mock)
|
||||
namespace = module.__dict__
|
||||
if "redis_client" in namespace:
|
||||
namespace["redis_client"] = redis_mock
|
||||
if "_pubsub_redis_client" in namespace:
|
||||
namespace["_pubsub_redis_client"] = redis_mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import sys
|
||||
from types import ModuleType
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.unit_tests.conftest import _patch_redis_clients_on_loaded_modules, redis_mock
|
||||
|
||||
|
||||
@pytest.mark.parametrize("attribute", ["redis_client", "_pubsub_redis_client"])
|
||||
def test_redis_rebinding_covers_new_globals_and_replaced_modules(
|
||||
monkeypatch: pytest.MonkeyPatch, attribute: str
|
||||
) -> None:
|
||||
name = "_redis_fixture_probe"
|
||||
module = ModuleType(name)
|
||||
monkeypatch.setitem(sys.modules, name, module)
|
||||
_patch_redis_clients_on_loaded_modules()
|
||||
|
||||
# A module can gain a reference after it was already seen by another test.
|
||||
setattr(module, attribute, object())
|
||||
_patch_redis_clients_on_loaded_modules()
|
||||
assert vars(module)[attribute] is redis_mock
|
||||
|
||||
# Rebind on every test, even when the set of imported modules is unchanged.
|
||||
setattr(module, attribute, object())
|
||||
_patch_redis_clients_on_loaded_modules()
|
||||
assert vars(module)[attribute] is redis_mock
|
||||
|
||||
replacement = ModuleType(name)
|
||||
setattr(replacement, attribute, object())
|
||||
monkeypatch.setitem(sys.modules, name, replacement)
|
||||
_patch_redis_clients_on_loaded_modules()
|
||||
assert vars(replacement)[attribute] is redis_mock
|
||||
|
||||
|
||||
def test_redis_rebinding_does_not_resolve_lazy_module_attributes(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
module = ModuleType("_lazy_redis_fixture_probe")
|
||||
|
||||
def resolve(name: str) -> object:
|
||||
pytest.fail(f"Redis rebinding must not import unrelated lazy attributes: {name}")
|
||||
|
||||
vars(module)["__getattr__"] = resolve
|
||||
monkeypatch.setitem(sys.modules, module.__name__, module)
|
||||
_patch_redis_clients_on_loaded_modules()
|
||||
Reference in new issue
Block a user