From 82973d690ccc7527e7e3af1c62a619d689cc9efa Mon Sep 17 00:00:00 2001 From: Axel PREL Date: Wed, 20 May 2026 11:17:00 +0200 Subject: [PATCH 1/2] [FIX] server_environment: no interpolation in the configparser Fixes #265 --- server_environment/readme/newsfragments/265.bugfix | 1 + server_environment/server_env.py | 2 +- server_environment/tests/test_server_environment.py | 2 +- server_environment/tests/testfiles/testing/base.conf | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 server_environment/readme/newsfragments/265.bugfix diff --git a/server_environment/readme/newsfragments/265.bugfix b/server_environment/readme/newsfragments/265.bugfix new file mode 100644 index 000000000..4e074060d --- /dev/null +++ b/server_environment/readme/newsfragments/265.bugfix @@ -0,0 +1 @@ +remove interpolation in the configparser diff --git a/server_environment/server_env.py b/server_environment/server_env.py index e1194d165..bfc7b7dea 100644 --- a/server_environment/server_env.py +++ b/server_environment/server_env.py @@ -155,7 +155,7 @@ def _load_config_from_env(config_p): def _load_config(): """Load the configuration and return a ConfigParser instance.""" - config_p = configparser.ConfigParser() + config_p = configparser.ConfigParser(interpolation=None) # options are case-sensitive config_p.optionxform = str diff --git a/server_environment/tests/test_server_environment.py b/server_environment/tests/test_server_environment.py index 822d4a7e0..d97076792 100644 --- a/server_environment/tests/test_server_environment.py +++ b/server_environment/tests/test_server_environment.py @@ -72,7 +72,7 @@ def test_value_retrieval(self): with self.set_config_dir("testfiles"): parser = server_env._load_config() val = parser.get("external_service.ftp", "user") - self.assertEqual(val, "testing") + self.assertEqual(val, "testing%") val = parser.get("external_service.ftp", "host") self.assertEqual(val, "sftp.example.com") diff --git a/server_environment/tests/testfiles/testing/base.conf b/server_environment/tests/testfiles/testing/base.conf index 46da78487..eeed2736f 100644 --- a/server_environment/tests/testfiles/testing/base.conf +++ b/server_environment/tests/testfiles/testing/base.conf @@ -2,4 +2,4 @@ odoo_test_option = Set in config file for testing env [external_service.ftp] -user = testing +user = testing% From 31497e6324a64cb4711ee5f1d828c08d077877c2 Mon Sep 17 00:00:00 2001 From: Axel PREL Date: Thu, 28 May 2026 10:26:53 +0200 Subject: [PATCH 2/2] [IMP] server_environment: configparser interpolation is configurable (default True) --- server_environment/readme/CONFIGURE.md | 19 +++++ .../readme/newsfragments/265.bugfix | 3 +- server_environment/server_env.py | 19 ++++- server_environment/tests/__init__.py | 1 + .../tests/test_config_interpolation.py | 70 +++++++++++++++++++ .../tests/test_server_environment.py | 2 +- .../tests/testfiles/testing/base.conf | 3 +- 7 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 server_environment/tests/test_config_interpolation.py diff --git a/server_environment/readme/CONFIGURE.md b/server_environment/readme/CONFIGURE.md index 7b119560f..5ade6f91b 100644 --- a/server_environment/readme/CONFIGURE.md +++ b/server_environment/readme/CONFIGURE.md @@ -106,3 +106,22 @@ Note: empty environment keys always take precedence over default fields Read the documentation of the class [models/server_env_mixin.py](models/server_env_mixin.py) and [models/server_env_tech_name_mixin.py] (models/server_env_tech_name_mixin.py) + +## ConfigParser interpolation + +By default, ConfigParser interpolation is enabled to preserve compatibility + +It can be disabled: + +:: + + [options] + server_environment_config_interpolation = False + +Or with the environment variable: + +:: + + export SERVER_ENV_CONFIG_INTERPOLATION=False + +When enabled, standard Python ``configparser`` interpolation rules apply. diff --git a/server_environment/readme/newsfragments/265.bugfix b/server_environment/readme/newsfragments/265.bugfix index 4e074060d..5ec376bc7 100644 --- a/server_environment/readme/newsfragments/265.bugfix +++ b/server_environment/readme/newsfragments/265.bugfix @@ -1 +1,2 @@ -remove interpolation in the configparser +make configparser interpolation parameter controllable via Odoo configuration +in 'server_environnment' diff --git a/server_environment/server_env.py b/server_environment/server_env.py index bfc7b7dea..94c2fcc19 100644 --- a/server_environment/server_env.py +++ b/server_environment/server_env.py @@ -154,8 +154,23 @@ def _load_config_from_env(config_p): def _load_config(): - """Load the configuration and return a ConfigParser instance.""" - config_p = configparser.ConfigParser(interpolation=None) + """ + Load the configuration and return a ConfigParser instance. + Uses the server_environment config param 'config_interpolation' + To know whether the config must be parsed with interpolation + of '%' characters. By default interpolation is enabled. + """ + interpolation = system_base_config.get( + "server_environment_config_interpolation", + os.environ.get("SERVER_ENV_CONFIG_INTERPOLATION", True), + ) + if isinstance(interpolation, str): + interpolation = _boolean_states.get(interpolation.lower(), True) + + if interpolation: + config_p = configparser.ConfigParser() + else: + config_p = configparser.ConfigParser(interpolation=None) # options are case-sensitive config_p.optionxform = str diff --git a/server_environment/tests/__init__.py b/server_environment/tests/__init__.py index 73093474f..559b8b136 100644 --- a/server_environment/tests/__init__.py +++ b/server_environment/tests/__init__.py @@ -1,2 +1,3 @@ from . import test_server_environment from . import test_environment_variable +from . import test_config_interpolation diff --git a/server_environment/tests/test_config_interpolation.py b/server_environment/tests/test_config_interpolation.py new file mode 100644 index 000000000..814753ca3 --- /dev/null +++ b/server_environment/tests/test_config_interpolation.py @@ -0,0 +1,70 @@ +# Copyright 2018 Camptocamp (https://www.camptocamp.com). +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +import logging +import os +from unittest.mock import patch + +from odoo.tools.config import config as odoo_config + +from .. import server_env +from . import common + +_logger = logging.getLogger(__name__) + + +class TestConfigInterpolation(common.ServerEnvironmentCase): + def test_interpolation_matrix(self): + cases = [ + ("default_default", None, None, True), + ("default_env_true", None, "True", True), + ("default_env_false", None, "False", False), + ("config_true_default", "True", None, True), + ("config_false_default", "False", None, False), + ("config_true_env_true", "True", "True", True), + ("config_true_env_false", "True", "False", True), + ("config_false_env_true", "False", "True", False), + ("config_false_env_false", "False", "False", False), + ] + + for name, config_value, env_value, expected in cases: + with self.subTest(name=name): + _logger.info(name) + self._assert_interpolation( + config_value, + env_value, + expected, + ) + + def _assert_interpolation( + self, + config_value, + env_value, + expected, + ): + config_patch = {"running_env": "testing"} + + if config_value is not None: + config_patch["server_environment_config_interpolation"] = config_value + + env_patch = {} + + if env_value is not None: + env_patch["SERVER_ENV_CONFIG_INTERPOLATION"] = env_value + + with ( + patch.dict(odoo_config.options, config_patch), + patch.dict(os.environ, env_patch), + self.set_config_dir("testfiles"), + ): + parser = server_env._load_config() + + val = parser.get( + "external_service.ftp", + "user", + ) + + if expected: + self.assertEqual(val, "testing") + else: + self.assertEqual(val, "%(base_user)s") diff --git a/server_environment/tests/test_server_environment.py b/server_environment/tests/test_server_environment.py index d97076792..822d4a7e0 100644 --- a/server_environment/tests/test_server_environment.py +++ b/server_environment/tests/test_server_environment.py @@ -72,7 +72,7 @@ def test_value_retrieval(self): with self.set_config_dir("testfiles"): parser = server_env._load_config() val = parser.get("external_service.ftp", "user") - self.assertEqual(val, "testing%") + self.assertEqual(val, "testing") val = parser.get("external_service.ftp", "host") self.assertEqual(val, "sftp.example.com") diff --git a/server_environment/tests/testfiles/testing/base.conf b/server_environment/tests/testfiles/testing/base.conf index eeed2736f..1a7158157 100644 --- a/server_environment/tests/testfiles/testing/base.conf +++ b/server_environment/tests/testfiles/testing/base.conf @@ -2,4 +2,5 @@ odoo_test_option = Set in config file for testing env [external_service.ftp] -user = testing% +base_user = testing +user = %(base_user)s