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 pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ optional-dependencies = { test-tools = [
"drf-spectacular (>=0.28.0,<1)",
"drf-writable-nested",
"environs (<16)",
"gunicorn (>=19.1)",
"gunicorn (>=23)",
"opentelemetry-instrumentation-django (>=0.46b0,<1)",
"opentelemetry-instrumentation-psycopg2 (>=0.46b0,<1)",
"opentelemetry-instrumentation-redis (>=0.46b0,<1)",
Expand Down
13 changes: 12 additions & 1 deletion src/common/gunicorn/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"accesslog": env.str("ACCESS_LOG_LOCATION", os.devnull),
"bind": "0.0.0.0:8000",
"config": "python:common.gunicorn.conf",
"keepalive": env.int("GUNICORN_KEEP_ALIVE", 2),
"logger_class": "common.gunicorn.logging.GunicornJsonCapableLogger",
"statsd_prefix": "flagsmith.api",
"threads": env.int("GUNICORN_THREADS", 1),
Expand All @@ -41,9 +42,19 @@ def __init__(self, options: dict[str, Any] | None) -> None:

def load_config(self) -> None:
cfg_settings = self.cfg.settings
env_args = self.cfg.parser().parse_args(self.cfg.get_cmd_args_from_env())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
env_options = {
key: value
for key, value in vars(env_args).items()
if key != "args" and value is not None
}
options_items = (
(key, value)
for key, value in {**GUNICORN_FLAGSMITH_DEFAULTS, **self.options}.items()
for key, value in {
**GUNICORN_FLAGSMITH_DEFAULTS,
**env_options,
**self.options,
}.items()
if key in cfg_settings
)
for key, value in options_items:
Expand Down
66 changes: 66 additions & 0 deletions tests/unit/common/gunicorn/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
import signal
import threading
import time
from collections.abc import Generator
from importlib import reload

import pytest
from drf_spectacular.generators import EndpointEnumerator
from pytest_mock import MockerFixture

import common.gunicorn.utils
from common.gunicorn.utils import (
DjangoWSGIApplication,
get_route_template,
Expand Down Expand Up @@ -34,9 +37,72 @@ def test_django_wsgi_application__defaults__expected_config(
== "common.gunicorn.logging.GunicornJsonCapableLogger"
)
assert settings["config"].value == "python:common.gunicorn.conf"
assert settings["keepalive"].value == 2
assert settings["threads"].value == 1
assert settings["timeout"].value == 30
assert settings["workers"].value == 1
assert app.load_wsgiapp() == wsgi_handler_mock


@pytest.fixture
def reload_gunicorn_utils() -> Generator[None, None, None]:
yield
reload(common.gunicorn.utils)


@pytest.mark.usefixtures("reload_gunicorn_utils")
def test_django_wsgi_application__env_vars_set__expected_config(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Given
monkeypatch.setenv("GUNICORN_KEEP_ALIVE", "65")
monkeypatch.setenv("GUNICORN_THREADS", "2")
monkeypatch.setenv("GUNICORN_TIMEOUT", "60")
monkeypatch.setenv("GUNICORN_WORKERS", "3")
utils = reload(common.gunicorn.utils)

# When
app = utils.DjangoWSGIApplication({})

# Then
settings = app.cfg.settings
assert settings["keepalive"].value == 65
assert settings["threads"].value == 2
assert settings["timeout"].value == 60
assert settings["workers"].value == 3


def test_django_wsgi_application__gunicorn_cmd_args_set__overrides_defaults(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Given
monkeypatch.setenv("GUNICORN_CMD_ARGS", "--keep-alive 65 --workers 4")

# When
app = DjangoWSGIApplication({})

# Then
settings = app.cfg.settings
assert settings["keepalive"].value == 65
assert settings["workers"].value == 4
assert settings["config"].value == "python:common.gunicorn.conf"


def test_django_wsgi_application__gunicorn_cmd_args_and_options_set__options_take_precedence(
monkeypatch: pytest.MonkeyPatch,
) -> None:
# Given
monkeypatch.setenv("GUNICORN_CMD_ARGS", "--keep-alive 65 --workers 4")

# When
app = DjangoWSGIApplication({"keepalive": 10})

# Then
settings = app.cfg.settings
assert settings["keepalive"].value == 10
assert settings["workers"].value == 4


def test_run_server__default_config_file__runs_expected(
unused_tcp_port: int,
mocker: MockerFixture,
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading