From a4886d19c3ca61850afa3708ac815183d71d15f5 Mon Sep 17 00:00:00 2001 From: James Kebinger Date: Thu, 16 Oct 2025 12:57:15 -0500 Subject: [PATCH 1/3] Update API key references to SDK key and bump version to 1.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename MissingApiKeyException to MissingSdkKeyException - Rename InvalidApiKeyException to InvalidSdkKeyException - Update error messages to reference SDK key instead of API key - Update test names and imports accordingly - Bump version from 1.0.0 to 1.0.1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- pyproject.toml | 2 +- sdk_reforge/VERSION | 2 +- sdk_reforge/options.py | 16 ++++++++-------- tests/test_options.py | 18 +++++++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 65d4253..3028dfb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "sdk-reforge" -version = "1.0.0" +version = "1.0.1" description = "Python sdk for Reforge Feature Flags and Config as a Service: https://www.reforge.com" license = "MIT" authors = ["Michael Berkowitz ", "James Kebinger "] diff --git a/sdk_reforge/VERSION b/sdk_reforge/VERSION index 3eefcb9..7dea76e 100644 --- a/sdk_reforge/VERSION +++ b/sdk_reforge/VERSION @@ -1 +1 @@ -1.0.0 +1.0.1 diff --git a/sdk_reforge/options.py b/sdk_reforge/options.py index e83ed39..8d3fafb 100644 --- a/sdk_reforge/options.py +++ b/sdk_reforge/options.py @@ -9,22 +9,22 @@ from .constants import ContextDictType -class MissingApiKeyException(Exception): +class MissingSdkKeyException(Exception): """ - Raised when no API key is found + Raised when no SDK key is found """ def __init__(self) -> None: - super().__init__("No API key found") + super().__init__("No SDK key found - set REFORGE_BACKEND_SDK_KEY") -class InvalidApiKeyException(Exception): +class InvalidSdkKeyException(Exception): """ - Raised when an invalid API key is provided + Raised when an invalid SDK key is provided """ def __init__(self, api_key: str) -> None: - super().__init__(f"Invalid API key: {api_key}") + super().__init__(f"Invalid SDK key: {api_key}") class InvalidApiUrlException(Exception): @@ -138,10 +138,10 @@ def __set_api_key(self, api_key: Optional[str]) -> None: return if api_key is None: - raise MissingApiKeyException() + raise MissingSdkKeyException() api_key = str(api_key).strip() if "-" not in api_key: - raise InvalidApiKeyException(api_key) + raise InvalidSdkKeyException(api_key) self.api_key = api_key self.api_key_id = api_key.split("-")[0] diff --git a/tests/test_options.py b/tests/test_options.py index 5df988d..1253040 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,7 +1,7 @@ from sdk_reforge import Options from sdk_reforge.options import ( - MissingApiKeyException, - InvalidApiKeyException, + MissingSdkKeyException, + InvalidSdkKeyException, InvalidApiUrlException, InvalidStreamUrlException, ) @@ -41,16 +41,16 @@ def test_api_key_from_input_overrides_env(self): assert options.api_key == "3-dev-api-key" assert options.api_key_id == "3" - def test_missing_api_key_error(self): - with pytest.raises(MissingApiKeyException) as context: + def test_missing_sdk_key_error(self): + with pytest.raises(MissingSdkKeyException) as context: Options() - assert "No API key found" in str(context) + assert "No SDK key found" in str(context) - def test_invalid_api_key_error(self): - with pytest.raises(InvalidApiKeyException) as context: - Options(sdk_key="bad_api_key") - assert "Invalid API key: bad_api_key" in str(context) + def test_invalid_sdk_key_error(self): + with pytest.raises(InvalidSdkKeyException) as context: + Options(sdk_key="bad_sdk_key") + assert "Invalid SDK key: bad_sdk_key" in str(context) def test_api_key_doesnt_matter_local_only_set_in_env(self): with extended_env({"REFORGE_DATASOURCES": "LOCAL_ONLY"}): From 653271d9ebc542188454d5853254a58456decd0a Mon Sep 17 00:00:00 2001 From: James Kebinger Date: Thu, 16 Oct 2025 13:20:24 -0500 Subject: [PATCH 2/3] Fix version lookup to read from VERSION file instead of importlib.metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove importlib.metadata dependency which was looking up wrong package name - Add _get_version() helper that reads directly from sdk_reforge/VERSION file - This works in both installed and development environments - Fixes PackageNotFoundError when package metadata is unavailable 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- sdk_reforge/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sdk_reforge/__init__.py b/sdk_reforge/__init__.py index bf36192..e782ac5 100644 --- a/sdk_reforge/__init__.py +++ b/sdk_reforge/__init__.py @@ -20,11 +20,11 @@ """ from typing import Optional +import os from . import _internal_logging from .options import Options as Options from .sdk import ReforgeSDK as ReforgeSDK -from importlib.metadata import version from .read_write_lock import ReadWriteLock as _ReadWriteLock from .context import Context, NamedContext from .feature_flag_sdk import FeatureFlagSDK @@ -52,6 +52,16 @@ log = _internal_logging.InternalLogger(__name__) +def _get_version() -> str: + """Get the SDK version from the VERSION file""" + try: + version_file = os.path.join(os.path.dirname(__file__), "VERSION") + with open(version_file, "r") as f: + return f.read().strip() + except Exception: + return "unknown" + + __base_sdk: Optional[ReforgeSDK] = None __options: Optional[Options] = None __lock = _ReadWriteLock() @@ -75,7 +85,7 @@ def get_sdk() -> ReforgeSDK: if not __options: raise Exception("Options has not been set") if not __base_sdk: - log.info(f"Initializing Reforge SDK version {version('reforge-python')}") + log.info(f"Initializing Reforge SDK version {_get_version()}") __base_sdk = ReforgeSDK(__options) return __base_sdk From 89ee7f59fd0add962fd11d16e24ab04dbdc02d60 Mon Sep 17 00:00:00 2001 From: James Kebinger Date: Thu, 16 Oct 2025 13:22:18 -0500 Subject: [PATCH 3/3] Cache version string in memory after first read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add __version_cache module-level variable - Read VERSION file only once, cache result for subsequent calls - Improves performance by avoiding repeated file I/O 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- sdk_reforge/__init__.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sdk_reforge/__init__.py b/sdk_reforge/__init__.py index e782ac5..d6c64f5 100644 --- a/sdk_reforge/__init__.py +++ b/sdk_reforge/__init__.py @@ -52,14 +52,23 @@ log = _internal_logging.InternalLogger(__name__) +__version_cache: Optional[str] = None + + def _get_version() -> str: - """Get the SDK version from the VERSION file""" + """Get the SDK version from the VERSION file (cached after first read)""" + global __version_cache + if __version_cache is not None: + return __version_cache + try: version_file = os.path.join(os.path.dirname(__file__), "VERSION") with open(version_file, "r") as f: - return f.read().strip() + __version_cache = f.read().strip() + return __version_cache except Exception: - return "unknown" + __version_cache = "unknown" + return __version_cache __base_sdk: Optional[ReforgeSDK] = None