Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/boost_weblate/settings_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def merge_boost_endpoint_throttle_rates(
globals()["REST_FRAMEWORK"] = merge_boost_endpoint_throttle_rates(_REST_FRAMEWORK)

_INSTALLED_APPS = globals().get("INSTALLED_APPS")
if _INSTALLED_APPS is not None:
if _INSTALLED_APPS is not None and _ENDPOINT_APP_CONFIG not in _INSTALLED_APPS:
# Tuple += creates a new object; assign back so exec namespace / settings see it.
# List += mutates in place, matching Weblate/Docker settings namespaces.
if isinstance(_INSTALLED_APPS, tuple):
Expand Down
136 changes: 136 additions & 0 deletions tests/test_settings_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,31 @@

import importlib
import importlib.util
import subprocess
import sys
import textwrap
from collections.abc import Callable
from pathlib import Path

import pytest

from boost_weblate.formats import registry

_ENDPOINT_APP_CONFIG = "boost_weblate.endpoint.apps.BoostEndpointConfig"
_REPO_ROOT = Path(__file__).resolve().parents[1]
_SETTINGS_OVERRIDE_PATH = _REPO_ROOT / "src/boost_weblate/settings_override.py"


def _exec_settings_override(namespace: dict) -> None:
exec(
compile(
_SETTINGS_OVERRIDE_PATH.read_text(encoding="utf-8"),
str(_SETTINGS_OVERRIDE_PATH),
"exec",
),
namespace,
)


def _plugin_weblate_paths() -> tuple[str, ...]:
return registry.weblate_class_paths()
Expand Down Expand Up @@ -143,3 +162,120 @@ def test_boost_task_timeout_settings_rejects_invalid_limits(
monkeypatch.setenv("BOOST_TASK_TIME_LIMIT", "900")
with pytest.raises(ValueError, match="BOOST_TASK_TIME_LIMIT"):
boost_task_timeout_settings()


@pytest.mark.parametrize(
"factory",
[
lambda: ["django.contrib.auth"],
lambda: ("django.contrib.auth",),
],
ids=["list", "tuple"],
)
def test_double_exec_does_not_duplicate_installed_apps(
factory: Callable[[], list[str] | tuple[str, ...]],
) -> None:
base_apps = factory()
ns: dict[str, object] = {"INSTALLED_APPS": base_apps}
_exec_settings_override(ns)
_exec_settings_override(ns)
apps = ns["INSTALLED_APPS"]
assert apps.count(_ENDPOINT_APP_CONFIG) == 1
assert apps[0] == "django.contrib.auth"
if isinstance(base_apps, tuple):
assert isinstance(apps, tuple)


def test_double_exec_does_not_double_ready_hooks() -> None:
script = textwrap.dedent(
f"""
import os
import sys
import tempfile
import types
from pathlib import Path

repo = Path({str(_REPO_ROOT)!r})
sys.path.insert(0, str(repo))
sys.path.insert(0, str(repo / "src"))

import weblate.settings_example as _wl_example

ns: dict[str, object] = {{}}
for _key, _value in _wl_example.__dict__.items():
if _key.isupper():
ns[_key] = _value

ns["INSTALLED_APPS"] = tuple(
app
for app in _wl_example.INSTALLED_APPS
if app != "django.contrib.postgres"
)

_data = tempfile.mkdtemp(prefix="double_exec_settings_")
ns["DATA_DIR"] = _data
ns["CACHE_DIR"] = os.path.join(_data, "cache")
ns["MEDIA_ROOT"] = os.path.join(_data, "media")
ns["STATIC_ROOT"] = os.path.join(_data, "static")
for _p in (ns["CACHE_DIR"], ns["MEDIA_ROOT"], ns["STATIC_ROOT"]):
os.makedirs(_p, exist_ok=True)

ns["DATABASES"] = {{
"default": {{
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(_data, "test.sqlite3"),
}}
}}
ns["SITE_DOMAIN"] = "test.invalid"
ns["DEBUG"] = False
ns["CELERY_TASK_ALWAYS_EAGER"] = True
ns["CELERY_BROKER_URL"] = "memory://"
ns["CELERY_TASK_EAGER_PROPAGATES"] = True
ns["CELERY_RESULT_BACKEND"] = None
ns["CACHES"] = {{
"default": {{"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
}}
ns["PASSWORD_HASHERS"] = ["django.contrib.auth.hashers.MD5PasswordHasher"]

override_path = Path({str(_SETTINGS_OVERRIDE_PATH)!r})
override_code = compile(
override_path.read_text(encoding="utf-8"),
str(override_path),
"exec",
)
exec(override_code, ns)
exec(override_code, ns)

settings_mod = types.ModuleType("tests._double_exec_settings")
for _key, _value in ns.items():
if _key.isupper():
setattr(settings_mod, _key, _value)
sys.modules["tests._double_exec_settings"] = settings_mod
os.environ["DJANGO_SETTINGS_MODULE"] = "tests._double_exec_settings"

from boost_weblate.endpoint.apps import BoostEndpointConfig

ready_calls: list[int] = []
_original_ready = BoostEndpointConfig.ready

def _counting_ready(self: BoostEndpointConfig) -> None:
ready_calls.append(1)
return _original_ready(self)

BoostEndpointConfig.ready = _counting_ready # type: ignore[method-assign]

import django

django.setup()
print(f"ready_calls={{len(ready_calls)}}")
"""
)
result = subprocess.run(
[sys.executable, "-c", script],
capture_output=True,
text=True,
cwd=_REPO_ROOT,
check=False,
)
assert result.returncode == 0, result.stderr
assert "ready_calls=1" in result.stdout
Loading