77from openfeature import api
88from openfeature .evaluation_context import EvaluationContext
99
10- FLAG_KEY = "test-boolean-variable"
11-
1210logger = logging .getLogger (__name__ )
1311
1412
1513def main ():
1614 """
1715 Sample usage of the DevCycle OpenFeature Provider along with the Python Server SDK using Local Bucketing.
16+
17+ This example demonstrates how to use all variable types supported by DevCycle through OpenFeature:
18+ - Boolean variables
19+ - String variables
20+ - Number variables (integer and float)
21+ - JSON object variables
22+
23+ See DEVCYCLE_SETUP.md for instructions on creating the required variables in DevCycle.
1824 """
1925 logging .basicConfig (level = "INFO" , format = "%(levelname)s: %(message)s" )
2026
2127 # create an instance of the DevCycle Client object
22- server_sdk_key = os .environ ["DEVCYCLE_SERVER_SDK_KEY" ]
28+ server_sdk_key = os .environ .get ("DEVCYCLE_SERVER_SDK_KEY" )
29+ if not server_sdk_key :
30+ logger .error ("DEVCYCLE_SERVER_SDK_KEY environment variable is not set" )
31+ logger .error ("Please set it with: export DEVCYCLE_SERVER_SDK_KEY='your-sdk-key'" )
32+ exit (1 )
33+
2334 devcycle_client = DevCycleLocalClient (server_sdk_key , DevCycleLocalOptions ())
2435
2536 # Wait for DevCycle to initialize and load the configuration
@@ -32,6 +43,8 @@ def main():
3243 logger .error ("DevCycle failed to initialize" )
3344 exit (1 )
3445
46+ logger .info ("DevCycle initialized successfully!\n " )
47+
3548 # set the provider for OpenFeature
3649 api .set_provider (devcycle_client .get_openfeature_provider ())
3750
@@ -53,22 +66,180 @@ def main():
5366 },
5467 )
5568
56- # Look up the value of the flag
57- if open_feature_client .get_boolean_value (FLAG_KEY , False , context ):
58- logger .info (f"Variable { FLAG_KEY } is enabled" )
69+ logger .info ("=" * 60 )
70+ logger .info ("Testing Boolean Variable" )
71+ logger .info ("=" * 60 )
72+
73+ # Test Boolean Variable
74+ boolean_details = open_feature_client .get_boolean_details (
75+ "test-boolean-variable" , False , context
76+ )
77+ logger .info (f"Variable Key: test-boolean-variable" )
78+ logger .info (f"Value: { boolean_details .value } " )
79+ logger .info (f"Reason: { boolean_details .reason } " )
80+ if boolean_details .value :
81+ logger .info ("✓ Boolean variable is ENABLED" )
5982 else :
60- logger .info (f"Variable { FLAG_KEY } is not enabled" )
61-
62- # Fetch a JSON object variable
63- json_object = open_feature_client .get_object_value (
83+ logger .info ("✗ Boolean variable is DISABLED" )
84+
85+ logger .info ("\n " + "=" * 60 )
86+ logger .info ("Testing String Variable" )
87+ logger .info ("=" * 60 )
88+
89+ # Test String Variable
90+ string_details = open_feature_client .get_string_details (
91+ "test-string-variable" , "default string" , context
92+ )
93+ logger .info (f"Variable Key: test-string-variable" )
94+ logger .info (f"Value: { string_details .value } " )
95+ logger .info (f"Reason: { string_details .reason } " )
96+
97+ logger .info ("\n " + "=" * 60 )
98+ logger .info ("Testing Number Variable (Integer)" )
99+ logger .info ("=" * 60 )
100+
101+ # Test Number Variable (Integer)
102+ integer_details = open_feature_client .get_integer_details (
103+ "test-number-variable" , 0 , context
104+ )
105+ logger .info (f"Variable Key: test-number-variable" )
106+ logger .info (f"Value: { integer_details .value } " )
107+ logger .info (f"Reason: { integer_details .reason } " )
108+
109+ logger .info ("\n " + "=" * 60 )
110+ logger .info ("Testing Number Variable (Float)" )
111+ logger .info ("=" * 60 )
112+
113+ # Test Number Variable as Float
114+ # Note: If the DevCycle variable is an integer, it will be cast to float
115+ float_value = open_feature_client .get_float_value (
116+ "test-number-variable" , 0.0 , context
117+ )
118+ logger .info (f"Variable Key: test-number-variable (as float)" )
119+ logger .info (f"Value: { float_value } " )
120+
121+ logger .info ("\n " + "=" * 60 )
122+ logger .info ("Testing JSON Object Variable" )
123+ logger .info ("=" * 60 )
124+
125+ # Test JSON Object Variable
126+ json_details = open_feature_client .get_object_details (
64127 "test-json-variable" , {"default" : "value" }, context
65128 )
66- logger .info (f"JSON Object Value: { json_object } " )
67-
68- # Retrieve a string variable along with resolution details
69- details = open_feature_client .get_string_details ("doesnt-exist" , "default" , context )
70- logger .info (f"String Value: { details .value } " )
71- logger .info (f"Eval Reason: { details .reason } " )
129+ logger .info (f"Variable Key: test-json-variable" )
130+ logger .info (f"Value: { json_details .value } " )
131+ logger .info (f"Reason: { json_details .reason } " )
132+
133+ logger .info ("\n " + "=" * 60 )
134+ logger .info ("Testing Object Variable - Empty Dictionary" )
135+ logger .info ("=" * 60 )
136+
137+ # Test with empty dictionary default (valid per OpenFeature spec)
138+ empty_dict_result = open_feature_client .get_object_value (
139+ "test-json-variable" , {}, context
140+ )
141+ logger .info (f"Variable Key: test-json-variable (with empty default)" )
142+ logger .info (f"Value: { empty_dict_result } " )
143+
144+ logger .info ("\n " + "=" * 60 )
145+ logger .info ("Testing Object Variable - Mixed Types" )
146+ logger .info ("=" * 60 )
147+
148+ # Test with flat dictionary containing mixed primitive types
149+ # OpenFeature allows string, int, float, bool, and None in flat dictionaries
150+ mixed_default = {
151+ "string_key" : "hello" ,
152+ "int_key" : 42 ,
153+ "float_key" : 3.14 ,
154+ "bool_key" : True ,
155+ "none_key" : None
156+ }
157+ mixed_result = open_feature_client .get_object_value (
158+ "test-json-variable" , mixed_default , context
159+ )
160+ logger .info (f"Variable Key: test-json-variable (with mixed types)" )
161+ logger .info (f"Value: { mixed_result } " )
162+ logger .info (f"Value types: { [(k , type (v ).__name__ ) for k , v in mixed_result .items ()]} " )
163+
164+ logger .info ("\n " + "=" * 60 )
165+ logger .info ("Testing Object Variable - All String Values" )
166+ logger .info ("=" * 60 )
167+
168+ # Test with all string values
169+ string_dict_default = {
170+ "name" : "John Doe" ,
171+ 172+ "status" : "active"
173+ }
174+ string_dict_result = open_feature_client .get_object_value (
175+ "test-json-variable" , string_dict_default , context
176+ )
177+ logger .info (f"Variable Key: test-json-variable (all strings)" )
178+ logger .info (f"Value: { string_dict_result } " )
179+
180+ logger .info ("\n " + "=" * 60 )
181+ logger .info ("Testing Object Variable - Numeric Values" )
182+ logger .info ("=" * 60 )
183+
184+ # Test with numeric values (integers and floats)
185+ numeric_dict_default = {
186+ "count" : 100 ,
187+ "percentage" : 85.5 ,
188+ "threshold" : 0
189+ }
190+ numeric_dict_result = open_feature_client .get_object_value (
191+ "test-json-variable" , numeric_dict_default , context
192+ )
193+ logger .info (f"Variable Key: test-json-variable (numeric)" )
194+ logger .info (f"Value: { numeric_dict_result } " )
195+
196+ logger .info ("\n " + "=" * 60 )
197+ logger .info ("Testing Object Variable - Boolean Flags" )
198+ logger .info ("=" * 60 )
199+
200+ # Test with boolean values
201+ bool_dict_default = {
202+ "feature_a" : True ,
203+ "feature_b" : False ,
204+ "feature_c" : True
205+ }
206+ bool_dict_result = open_feature_client .get_object_value (
207+ "test-json-variable" , bool_dict_default , context
208+ )
209+ logger .info (f"Variable Key: test-json-variable (booleans)" )
210+ logger .info (f"Value: { bool_dict_result } " )
211+
212+ logger .info ("\n " + "=" * 60 )
213+ logger .info ("Testing Object Variable - With None Values" )
214+ logger .info ("=" * 60 )
215+
216+ # Test with None values (valid per OpenFeature spec for flat dictionaries)
217+ none_dict_default = {
218+ "optional_field" : None ,
219+ "required_field" : "value" ,
220+ "nullable_count" : None
221+ }
222+ none_dict_result = open_feature_client .get_object_value (
223+ "test-json-variable" , none_dict_default , context
224+ )
225+ logger .info (f"Variable Key: test-json-variable (with None)" )
226+ logger .info (f"Value: { none_dict_result } " )
227+
228+ logger .info ("\n " + "=" * 60 )
229+ logger .info ("Testing Non-Existent Variable (Should Return Default)" )
230+ logger .info ("=" * 60 )
231+
232+ # Test non-existent variable to demonstrate default handling
233+ nonexistent_details = open_feature_client .get_string_details (
234+ "doesnt-exist" , "default fallback value" , context
235+ )
236+ logger .info (f"Variable Key: doesnt-exist" )
237+ logger .info (f"Value: { nonexistent_details .value } " )
238+ logger .info (f"Reason: { nonexistent_details .reason } " )
239+
240+ logger .info ("\n " + "=" * 60 )
241+ logger .info ("All tests completed!" )
242+ logger .info ("=" * 60 )
72243
73244 devcycle_client .close ()
74245
0 commit comments