From ff1a61ea83fa07b68d4de680338cceb5a1521008 Mon Sep 17 00:00:00 2001 From: Shiva Guntoju Date: Tue, 7 Apr 2026 09:51:05 +0530 Subject: [PATCH 1/2] Warn when Config singleton is re-requested with different parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BaseConfig.__init__ now detects when an already-initialized singleton is called with different region, timeout, runtime_base_url, or management_base_url and logs a WARNING with the mismatched values. This surfaces a silent failure mode where callers unknowingly get back a Config with different settings than requested. The singleton behavior is preserved — this only adds visibility. Fixes: AIFW-21679 Made-with: Cursor --- aidefense/config.py | 39 ++++++++++++++++++++++++++++++++++ aidefense/tests/test_config.py | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/aidefense/config.py b/aidefense/config.py index 79402ef..78971be 100644 --- a/aidefense/config.py +++ b/aidefense/config.py @@ -83,6 +83,8 @@ def __new__(cls, *args, **kwargs): return cls._instances[cls] + _logger = logging.getLogger("aidefense_sdk.config") + def __init__(self, *args, **kwargs): # Double-checked locking: fast path avoids the lock for already-init'd # singletons; the lock prevents concurrent first-time callers from both @@ -96,6 +98,8 @@ def __init__(self, *args, **kwargs): except Exception: self._instances.pop(type(self), None) raise + elif kwargs: + self._warn_if_params_differ(**kwargs) def _set_region(self, region: str): if not isinstance(region, str): @@ -178,6 +182,41 @@ def _set_pool_config(self, pool_config: dict): "pool_maxsize": pool_config.get("pool_maxsize", self.DEFAULT_POOL_MAXSIZE), } + def _warn_if_params_differ(self, **kwargs): + """Log a warning when the singleton is re-requested with different parameters.""" + _FIELDS = { + "region": "_normalize_requested_region", + "timeout": None, + "runtime_base_url": None, + "management_base_url": None, + } + diffs = [] + for key, normalizer in _FIELDS.items(): + if key not in kwargs or kwargs[key] is None: + continue + requested = kwargs[key] + if normalizer and hasattr(self, normalizer): + requested = getattr(self, normalizer)(requested) + current = getattr(self, key, None) + if current is not None and requested != current: + diffs.append(f"{key}={current!r} (requested {requested!r})") + if diffs: + self._logger.warning( + "%s singleton already initialized. Ignoring different " + "parameters: %s. Construct %s once and share it, or clear " + "%s._instances to re-initialize.", + type(self).__name__, + ", ".join(diffs), + type(self).__name__, + type(self).__name__, + ) + + @staticmethod + def _normalize_requested_region(region): + """Map short-code regions to canonical names for comparison.""" + _SHORT = {"us": "us-west-2", "eu": "eu-central-1", "apj": "ap-northeast-1"} + return _SHORT.get(region, region) if isinstance(region, str) else region + @abstractmethod def _initialize(self, *args, **kwargs): pass diff --git a/aidefense/tests/test_config.py b/aidefense/tests/test_config.py index 4618f41..ad7d7ac 100644 --- a/aidefense/tests/test_config.py +++ b/aidefense/tests/test_config.py @@ -75,3 +75,42 @@ def test_config_with_pool_config(): config = Config(pool_config=pool_conf) assert config.connection_pool._pool_connections == 3 assert config.connection_pool._pool_maxsize == 7 + + +def test_config_warns_on_different_region(caplog): + Config(region="us-west-2") + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config(region="eu-central-1") + assert "already initialized" in caplog.text + assert "region" in caplog.text + assert "eu-central-1" in caplog.text + + +def test_config_warns_on_different_timeout(caplog): + Config(timeout=30) + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config(timeout=60) + assert "already initialized" in caplog.text + assert "timeout" in caplog.text + + +def test_config_no_warning_on_same_params(caplog): + Config(region="us-west-2", timeout=30) + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config(region="us-west-2", timeout=30) + assert "already initialized" not in caplog.text + + +def test_config_no_warning_on_short_region_alias(caplog): + """'us' normalizes to 'us-west-2', so no mismatch warning.""" + Config(region="us-west-2") + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config(region="us") + assert "already initialized" not in caplog.text + + +def test_config_no_warning_when_no_kwargs(caplog): + Config(region="eu-central-1") + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config() + assert "already initialized" not in caplog.text From 760dc92777e4a5e4351e721ff1398fb696581895 Mon Sep 17 00:00:00 2001 From: Shiva Guntoju Date: Tue, 7 Apr 2026 09:57:55 +0530 Subject: [PATCH 2/2] Address PR review: detect positional args and normalize URLs - Merge positional args into the comparison so Config("eu-central-1") triggers the warning just like Config(region="eu-central-1") - Normalize URLs by stripping trailing slashes before comparison to avoid false-positive warnings when the same effective URL is passed Adds regression tests for both cases. Made-with: Cursor --- aidefense/config.py | 40 ++++++++++++++++++++++------------ aidefense/tests/test_config.py | 17 +++++++++++++++ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/aidefense/config.py b/aidefense/config.py index 78971be..d2e58ce 100644 --- a/aidefense/config.py +++ b/aidefense/config.py @@ -98,8 +98,8 @@ def __init__(self, *args, **kwargs): except Exception: self._instances.pop(type(self), None) raise - elif kwargs: - self._warn_if_params_differ(**kwargs) + elif args or kwargs: + self._warn_if_params_differ(*args, **kwargs) def _set_region(self, region: str): if not isinstance(region, str): @@ -182,24 +182,31 @@ def _set_pool_config(self, pool_config: dict): "pool_maxsize": pool_config.get("pool_maxsize", self.DEFAULT_POOL_MAXSIZE), } - def _warn_if_params_differ(self, **kwargs): + _INIT_PARAM_NAMES = ( + "region", "runtime_base_url", "management_base_url", "timeout", + ) + + def _warn_if_params_differ(self, *args, **kwargs): """Log a warning when the singleton is re-requested with different parameters.""" - _FIELDS = { - "region": "_normalize_requested_region", - "timeout": None, - "runtime_base_url": None, - "management_base_url": None, + merged = dict(zip(self._INIT_PARAM_NAMES, args)) + merged.update(kwargs) + + _NORMALIZERS = { + "region": self._normalize_requested_region, + "runtime_base_url": self._normalize_url, + "management_base_url": self._normalize_url, } diffs = [] - for key, normalizer in _FIELDS.items(): - if key not in kwargs or kwargs[key] is None: + for key in self._INIT_PARAM_NAMES: + if key not in merged or merged[key] is None: continue - requested = kwargs[key] - if normalizer and hasattr(self, normalizer): - requested = getattr(self, normalizer)(requested) + requested = merged[key] + normalizer = _NORMALIZERS.get(key) + if normalizer: + requested = normalizer(requested) current = getattr(self, key, None) if current is not None and requested != current: - diffs.append(f"{key}={current!r} (requested {requested!r})") + diffs.append(f"{key}={current!r} (requested {merged[key]!r})") if diffs: self._logger.warning( "%s singleton already initialized. Ignoring different " @@ -217,6 +224,11 @@ def _normalize_requested_region(region): _SHORT = {"us": "us-west-2", "eu": "eu-central-1", "apj": "ap-northeast-1"} return _SHORT.get(region, region) if isinstance(region, str) else region + @staticmethod + def _normalize_url(url): + """Strip trailing slash to match stored value normalization.""" + return url.rstrip("/") if isinstance(url, str) else url + @abstractmethod def _initialize(self, *args, **kwargs): pass diff --git a/aidefense/tests/test_config.py b/aidefense/tests/test_config.py index ad7d7ac..5b51d80 100644 --- a/aidefense/tests/test_config.py +++ b/aidefense/tests/test_config.py @@ -114,3 +114,20 @@ def test_config_no_warning_when_no_kwargs(caplog): with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): Config() assert "already initialized" not in caplog.text + + +def test_config_warns_on_positional_region_mismatch(caplog): + """Positional args (not just kwargs) must trigger the warning.""" + Config("us-west-2") + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config("eu-central-1") + assert "already initialized" in caplog.text + assert "region" in caplog.text + + +def test_config_no_false_positive_on_trailing_slash_url(caplog): + """URLs with trailing slashes should be normalized before comparison.""" + Config(runtime_base_url="https://custom.endpoint.com/") + with caplog.at_level(logging.WARNING, logger="aidefense_sdk.config"): + Config(runtime_base_url="https://custom.endpoint.com/") + assert "already initialized" not in caplog.text