From 38477811c27d1279d1dd2d81688afa0f2c886cbb Mon Sep 17 00:00:00 2001 From: platreth Date: Thu, 9 Jul 2026 11:33:31 +0200 Subject: [PATCH 1/3] refactor(settings): standardize Constance env overrides to CONSTANCE_ prefix Rename Constance override env vars to a single CONSTANCE_ prefix: - KOBO_SUPPORT_EMAIL/URL, KOBO_ACADEMY_URL, KOBO_COMMUNITY_URL - USAGE_LIMIT_ENFORCEMENT (env fallback + pytest env) BREAKING: deployments setting the old names must rename them. No backward-compat fallback (matches the existing ASR_MT_GOOGLE_* precedent). --- kobo/settings/base.py | 17 ++++++++--------- pyproject.toml | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/kobo/settings/base.py b/kobo/settings/base.py index af112352d2..b0385abd1a 100644 --- a/kobo/settings/base.py +++ b/kobo/settings/base.py @@ -225,21 +225,24 @@ 'in the user interface', ), 'SUPPORT_EMAIL': ( - env.str('KOBO_SUPPORT_EMAIL', env.str('DEFAULT_FROM_EMAIL', 'help@kobotoolbox.org')), + env.str( + 'CONSTANCE_SUPPORT_EMAIL', + env.str('DEFAULT_FROM_EMAIL', 'help@kobotoolbox.org'), + ), 'Email address for users to contact, e.g. when they encounter ' 'unhandled errors in the application', ), 'SUPPORT_URL': ( - env.str('KOBO_SUPPORT_URL', 'https://support.kobotoolbox.org/'), + env.str('CONSTANCE_SUPPORT_URL', 'https://support.kobotoolbox.org/'), 'URL for "KoboToolbox Help Center"', ), 'ACADEMY_URL': ( - env.str('KOBO_ACADEMY_URL', 'https://academy.kobotoolbox.org/'), + env.str('CONSTANCE_ACADEMY_URL', 'https://academy.kobotoolbox.org/'), 'URL for "KoboToolbox Community Forum"', ), 'COMMUNITY_URL': ( env.str( - 'KOBO_COMMUNITY_URL', 'https://community.kobotoolbox.org/' + 'CONSTANCE_COMMUNITY_URL', 'https://community.kobotoolbox.org/' ), 'URL for "KoboToolbox Community Forum"', ), @@ -328,7 +331,7 @@ 'Require MFA for superusers with a usable password', ), 'USAGE_LIMIT_ENFORCEMENT': ( - env.bool('USAGE_LIMIT_ENFORCEMENT', False), + env.bool('CONSTANCE_USAGE_LIMIT_ENFORCEMENT', False), 'For Stripe-enabled instances, determines whether usage limits will be enforced' 'by blocking submissions/NLP actions or deleting stored files.', ), @@ -345,10 +348,6 @@ ' the operations API. ' ) ), - # We are adopting the `CONSTANCE_` prefix as the new standard for all env - # variables that override Constance defaults. Legacy variables (e.g., KOBO_*) - # will be deprecated and migrated to this new pattern after the upcoming - # migration to Constance 4.x 'ASR_MT_GOOGLE_PROJECT_ID': ( env.str('CONSTANCE_ASR_MT_GOOGLE_PROJECT_ID', 'kobo-asr-mt'), 'ID of the Google Cloud project used to access ASR/MT APIs', diff --git a/pyproject.toml b/pyproject.toml index 5bb6812954..86199b39e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,11 +57,11 @@ testpaths = [ 'kpi', 'hub', ] -# Add USAGE_LIMIT_ENFORCEMENT here because setting it in +# Add CONSTANCE_USAGE_LIMIT_ENFORCEMENT here because setting it in # test module doesn't get registered by constance env = [ 'DJANGO_SETTINGS_MODULE=kobo.settings.testing', - 'USAGE_LIMIT_ENFORCEMENT=True', + 'CONSTANCE_USAGE_LIMIT_ENFORCEMENT=True', ] addopts = [ '-m not performance', From 3f18e14ad11daaec4e0d0386871ba71391f89022 Mon Sep 17 00:00:00 2001 From: platreth Date: Thu, 9 Jul 2026 17:02:23 +0200 Subject: [PATCH 2/3] refactor(settings): keep backward-compat for deprecated Constance env names Per review: read the CONSTANCE_-prefixed var but fall back to the deprecated name (KOBO_*/bare), emitting a DeprecationWarning when the old name is still set. Adds the _constance_env helper (matches the existing KPI_BROKER_URL -> CELERY_BROKER_URL deprecation idiom). --- kobo/settings/base.py | 50 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/kobo/settings/base.py b/kobo/settings/base.py index b0385abd1a..c80ba28093 100644 --- a/kobo/settings/base.py +++ b/kobo/settings/base.py @@ -190,6 +190,24 @@ # must use `constance.config.THE_SETTING` instead of # `django.conf.settings.THE_SETTING` + +def _constance_env(getter, new_key, deprecated_key, default): + """ + Read a Constance override from the `CONSTANCE_`-prefixed environment + variable, falling back to its deprecated (unprefixed / `KOBO_`) name. + + Emit a `DeprecationWarning` when the deprecated name is still set so + deployments know to migrate to the `CONSTANCE_` prefix. + """ + if deprecated_key in os.environ: + warnings.warn( + f'{deprecated_key} is renamed {new_key}, ' + f'update the environment variable.', + DeprecationWarning, + ) + return getter(new_key, getter(deprecated_key, default)) + + CONSTANCE_CONFIG = { 'REGISTRATION_OPEN': ( True, @@ -225,24 +243,39 @@ 'in the user interface', ), 'SUPPORT_EMAIL': ( - env.str( + _constance_env( + env.str, 'CONSTANCE_SUPPORT_EMAIL', + 'KOBO_SUPPORT_EMAIL', env.str('DEFAULT_FROM_EMAIL', 'help@kobotoolbox.org'), ), 'Email address for users to contact, e.g. when they encounter ' 'unhandled errors in the application', ), 'SUPPORT_URL': ( - env.str('CONSTANCE_SUPPORT_URL', 'https://support.kobotoolbox.org/'), + _constance_env( + env.str, + 'CONSTANCE_SUPPORT_URL', + 'KOBO_SUPPORT_URL', + 'https://support.kobotoolbox.org/', + ), 'URL for "KoboToolbox Help Center"', ), 'ACADEMY_URL': ( - env.str('CONSTANCE_ACADEMY_URL', 'https://academy.kobotoolbox.org/'), + _constance_env( + env.str, + 'CONSTANCE_ACADEMY_URL', + 'KOBO_ACADEMY_URL', + 'https://academy.kobotoolbox.org/', + ), 'URL for "KoboToolbox Community Forum"', ), 'COMMUNITY_URL': ( - env.str( - 'CONSTANCE_COMMUNITY_URL', 'https://community.kobotoolbox.org/' + _constance_env( + env.str, + 'CONSTANCE_COMMUNITY_URL', + 'KOBO_COMMUNITY_URL', + 'https://community.kobotoolbox.org/', ), 'URL for "KoboToolbox Community Forum"', ), @@ -331,7 +364,12 @@ 'Require MFA for superusers with a usable password', ), 'USAGE_LIMIT_ENFORCEMENT': ( - env.bool('CONSTANCE_USAGE_LIMIT_ENFORCEMENT', False), + _constance_env( + env.bool, + 'CONSTANCE_USAGE_LIMIT_ENFORCEMENT', + 'USAGE_LIMIT_ENFORCEMENT', + False, + ), 'For Stripe-enabled instances, determines whether usage limits will be enforced' 'by blocking submissions/NLP actions or deleting stored files.', ), From 078be154fb6117474546e538c5552fea04ccf6de Mon Sep 17 00:00:00 2001 From: platreth Date: Tue, 14 Jul 2026 09:54:01 +0200 Subject: [PATCH 3/3] refactor(settings): move settings helpers to kobo/settings/utils.py DEV-1976 --- kobo/settings/base.py | 39 ++++++--------------------------------- kobo/settings/utils.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 kobo/settings/utils.py diff --git a/kobo/settings/base.py b/kobo/settings/base.py index c80ba28093..c160849b3c 100644 --- a/kobo/settings/base.py +++ b/kobo/settings/base.py @@ -18,6 +18,7 @@ from kpi.constants import PERM_DELETE_ASSET, PERM_MANAGE_ASSET from ..static_lists import EXTRA_LANG_INFO, SECTOR_CHOICE_DEFAULTS +from .utils import constance_env, dj_stripe_request_callback_method env = environ.Env() @@ -190,24 +191,6 @@ # must use `constance.config.THE_SETTING` instead of # `django.conf.settings.THE_SETTING` - -def _constance_env(getter, new_key, deprecated_key, default): - """ - Read a Constance override from the `CONSTANCE_`-prefixed environment - variable, falling back to its deprecated (unprefixed / `KOBO_`) name. - - Emit a `DeprecationWarning` when the deprecated name is still set so - deployments know to migrate to the `CONSTANCE_` prefix. - """ - if deprecated_key in os.environ: - warnings.warn( - f'{deprecated_key} is renamed {new_key}, ' - f'update the environment variable.', - DeprecationWarning, - ) - return getter(new_key, getter(deprecated_key, default)) - - CONSTANCE_CONFIG = { 'REGISTRATION_OPEN': ( True, @@ -243,7 +226,7 @@ def _constance_env(getter, new_key, deprecated_key, default): 'in the user interface', ), 'SUPPORT_EMAIL': ( - _constance_env( + constance_env( env.str, 'CONSTANCE_SUPPORT_EMAIL', 'KOBO_SUPPORT_EMAIL', @@ -253,7 +236,7 @@ def _constance_env(getter, new_key, deprecated_key, default): 'unhandled errors in the application', ), 'SUPPORT_URL': ( - _constance_env( + constance_env( env.str, 'CONSTANCE_SUPPORT_URL', 'KOBO_SUPPORT_URL', @@ -262,7 +245,7 @@ def _constance_env(getter, new_key, deprecated_key, default): 'URL for "KoboToolbox Help Center"', ), 'ACADEMY_URL': ( - _constance_env( + constance_env( env.str, 'CONSTANCE_ACADEMY_URL', 'KOBO_ACADEMY_URL', @@ -271,7 +254,7 @@ def _constance_env(getter, new_key, deprecated_key, default): 'URL for "KoboToolbox Community Forum"', ), 'COMMUNITY_URL': ( - _constance_env( + constance_env( env.str, 'CONSTANCE_COMMUNITY_URL', 'KOBO_COMMUNITY_URL', @@ -364,7 +347,7 @@ def _constance_env(getter, new_key, deprecated_key, default): 'Require MFA for superusers with a usable password', ), 'USAGE_LIMIT_ENFORCEMENT': ( - _constance_env( + constance_env( env.bool, 'CONSTANCE_USAGE_LIMIT_ENFORCEMENT', 'USAGE_LIMIT_ENFORCEMENT', @@ -1292,16 +1275,6 @@ def __init__(self, *args, **kwargs): STRIPE_ENABLED = env.bool('STRIPE_ENABLED', False) -def dj_stripe_request_callback_method(): - # This method exists because dj-stripe's documentation doesn't reflect reality. - # It claims that DJSTRIPE_SUBSCRIBER_MODEL no longer needs a request callback but - # this error occurs without it: `DJSTRIPE_SUBSCRIBER_MODEL_REQUEST_CALLBACK must - # be implemented if a DJSTRIPE_SUBSCRIBER_MODEL is defined` - # It doesn't need to do anything other than exist - # https://github.com/dj-stripe/dj-stripe/issues/1900 - pass - - BULK_ACTION_RATE_LIMITS = { 'automatic_google_transcription': { 'max_jobs_per_minute': 120, diff --git a/kobo/settings/utils.py b/kobo/settings/utils.py new file mode 100644 index 0000000000..7d04d4bef7 --- /dev/null +++ b/kobo/settings/utils.py @@ -0,0 +1,35 @@ +import os +import warnings +from typing import Any, Callable + + +def constance_env( + getter: Callable, + new_key: str, + deprecated_key: str, + default: Any, +) -> Any: + """ + Read a Constance override from the `CONSTANCE_`-prefixed environment + variable, falling back to its deprecated (unprefixed / `KOBO_`) name. + + Emit a `DeprecationWarning` when the deprecated name is still set so + deployments know to migrate to the `CONSTANCE_` prefix. + """ + if deprecated_key in os.environ: + warnings.warn( + f'{deprecated_key} is renamed {new_key}, ' + f'update the environment variable.', + DeprecationWarning, + ) + return getter(new_key, getter(deprecated_key, default)) + + +def dj_stripe_request_callback_method(): + # This method exists because dj-stripe's documentation doesn't reflect reality. + # It claims that DJSTRIPE_SUBSCRIBER_MODEL no longer needs a request callback but + # this error occurs without it: `DJSTRIPE_SUBSCRIBER_MODEL_REQUEST_CALLBACK must + # be implemented if a DJSTRIPE_SUBSCRIBER_MODEL is defined` + # It doesn't need to do anything other than exist + # https://github.com/dj-stripe/dj-stripe/issues/1900 + pass