diff --git a/prefab_cloud_python/__init__.py b/prefab_cloud_python/__init__.py index a81bd6c..ecfdea6 100644 --- a/prefab_cloud_python/__init__.py +++ b/prefab_cloud_python/__init__.py @@ -30,6 +30,12 @@ from .context import Context, NamedContext from .feature_flag_client import FeatureFlagClient from .config_client import ConfigClient +from .constants import ( + ConfigValueType, + ContextDictType, + ContextDictOrContext, + NoDefaultProvided, +) # Re-export Protocol Buffer types for easier access import prefab_pb2 diff --git a/prefab_cloud_python/_telemetry.py b/prefab_cloud_python/_telemetry.py index fb721bc..fba2836 100644 --- a/prefab_cloud_python/_telemetry.py +++ b/prefab_cloud_python/_telemetry.py @@ -304,8 +304,11 @@ def run(self): try: super().run() except Exception as e: - # ignore exception so thread keeps running - logger.exception(f"Exception in thread {self.name}: {e}") + # Log just the exception name and message without the full traceback + logger.warning( + f"Exception in thread {self.name}: {e.__class__.__name__}: {e}" + ) + # Using warning level instead of error+traceback to keep logs cleaner def __init__( self, diff --git a/prefab_cloud_python/client.py b/prefab_cloud_python/client.py index 8356ff9..9d6dfee 100644 --- a/prefab_cloud_python/client.py +++ b/prefab_cloud_python/client.py @@ -22,7 +22,11 @@ import uuid import requests from urllib.parse import urljoin -from .constants import NoDefaultProvided, ConfigValueType, ContextDictType +from .constants import ( + NoDefaultProvided, + ConfigValueType, + ContextDictOrContext, +) from ._internal_constants import LOG_LEVEL_BASE_KEY PostBodyType = Union[Prefab.Loggers, Prefab.ContextShapes, Prefab.TelemetryEvents] @@ -78,7 +82,7 @@ def get( self, key: str, default: ConfigValueType = NoDefaultProvided, - context: Optional[ContextDictType | Context] = None, + context: Optional[ContextDictOrContext] = None, ) -> ConfigValueType: if self.is_ff(key): return self.feature_flag_client().get(key, default=default, context=context) @@ -86,7 +90,7 @@ def get( return self.config_client().get(key, default=default, context=context) def enabled( - self, feature_name: str, context: Optional[ContextDictType | Context] = None + self, feature_name: str, context: Optional[ContextDictOrContext] = None ) -> bool: return self.feature_flag_client().feature_is_on_for( feature_name, context=context @@ -163,7 +167,7 @@ def is_ready(self) -> bool: return self.config_client().is_ready() def set_global_context( - self, global_context: Optional[ContextDictType | Context] = None + self, global_context: Optional[ContextDictOrContext] = None ) -> Client: self.global_context = Context.normalize_context_arg(global_context) return self diff --git a/prefab_cloud_python/config_value_wrapper.py b/prefab_cloud_python/config_value_wrapper.py index 22d0524..5205d8d 100644 --- a/prefab_cloud_python/config_value_wrapper.py +++ b/prefab_cloud_python/config_value_wrapper.py @@ -7,8 +7,9 @@ class ConfigValueWrapper: @staticmethod def wrap(value, confidential=None): value_type = type(value) - - if value_type == int: + if value_type == Prefab.ConfigValue: + return value + elif value_type == int: return Prefab.ConfigValue(int=value, confidential=confidential) elif value_type == float: return Prefab.ConfigValue(double=value, confidential=confidential) diff --git a/prefab_cloud_python/constants.py b/prefab_cloud_python/constants.py index 598da28..16ee334 100644 --- a/prefab_cloud_python/constants.py +++ b/prefab_cloud_python/constants.py @@ -1,6 +1,17 @@ -from typing import Optional, Union -from datetime import timedelta +from typing import Optional, Union, TYPE_CHECKING +from datetime import timedelta, date, datetime + +import prefab_pb2 as Prefab + +if TYPE_CHECKING: + from .context import Context NoDefaultProvided = object() ConfigValueType = Optional[Union[int, float, bool, str, list[str], timedelta, dict]] -ContextDictType = dict[str, dict[str, ConfigValueType]] +ContextDictType = dict[ + str, + dict[ + str, Union[int, float, bool, str, date, datetime, list[str], Prefab.ConfigValue] + ], +] +ContextDictOrContext = Union[ContextDictType, "Context"] diff --git a/tests/test_context.py b/tests/test_context.py index 8577400..39b58e5 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -169,6 +169,29 @@ def test_named_context_to_proto(self): ) assert proto_context == expected_proto_context + def test_named_context_to_proto_with_config_values(self): + proto_context = NamedContext( + "the-name", + { + "a": ConfigValue(int=10), + "b": ConfigValue(double=1.1), + "c": "hello world", + "d": ["hello", "world"], + "e": True, + }, + ).to_proto() + expected_proto_context = ProtoContext( + type="the-name", + values={ + "a": ConfigValue(int=10), + "b": ConfigValue(double=1.1), + "c": ConfigValue(string="hello world"), + "d": ConfigValue(string_list=StringList(values=["hello", "world"])), + "e": ConfigValue(bool=True), + }, + ) + assert proto_context == expected_proto_context + def test_context_to_proto(self): proto_context_set = Context( {