chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1671 @@
1
+ ---
2
+ name: feature-flags
3
+ description: "LaunchDarkly Python Server SDK for feature flag management and experimentation"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "9.12.3"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "launchdarkly,feature-flags,toggles,experimentation,rollout"
10
+ ---
11
+
12
+ # LaunchDarkly Python Server SDK
13
+
14
+ ## Golden Rule
15
+
16
+ **Always use `launchdarkly-server-sdk` for server-side Python applications.**
17
+
18
+ ```bash
19
+ pip install launchdarkly-server-sdk
20
+ ```
21
+
22
+ Do NOT use:
23
+ - `launchdarkly-client` (deprecated, last updated 2019)
24
+ - `launchdarkly-python-client` (unofficial package)
25
+ - Any unofficial or third-party LaunchDarkly packages
26
+
27
+ The official package is `launchdarkly-server-sdk` maintained by LaunchDarkly.
28
+
29
+ Requires Python 3.9 or higher (as of v9.12+).
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install launchdarkly-server-sdk
37
+ ```
38
+
39
+ Optional observability plugin (requires v9.12+):
40
+
41
+ ```bash
42
+ pip install launchdarkly-observability
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Environment Variables
48
+
49
+ Set your SDK key as an environment variable:
50
+
51
+ ```bash
52
+ export LAUNCHDARKLY_SDK_KEY="sdk-key-123abc"
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Initialization
58
+
59
+ ### Basic Initialization
60
+
61
+ ```python
62
+ import ldclient
63
+ from ldclient.config import Config
64
+ import os
65
+
66
+ ldclient.set_config(Config(os.environ['LAUNCHDARKLY_SDK_KEY']))
67
+ client = ldclient.get()
68
+
69
+ # Wait for initialization
70
+ if client.is_initialized():
71
+ print('LaunchDarkly client initialized')
72
+ else:
73
+ print('LaunchDarkly client failed to initialize')
74
+ ```
75
+
76
+ ### With Timeout
77
+
78
+ ```python
79
+ import ldclient
80
+ from ldclient.config import Config
81
+ import os
82
+
83
+ ldclient.set_config(Config(os.environ['LAUNCHDARKLY_SDK_KEY']))
84
+ client = ldclient.get()
85
+
86
+ # Wait up to 10 seconds for initialization
87
+ if client.wait_for_initialization(10):
88
+ print('Client initialized successfully')
89
+ else:
90
+ print('Client initialization timeout')
91
+ ```
92
+
93
+ ### With Configuration Options
94
+
95
+ ```python
96
+ import ldclient
97
+ from ldclient.config import Config
98
+ import os
99
+
100
+ config = Config(
101
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
102
+ stream=True,
103
+ connect_timeout=10,
104
+ read_timeout=15,
105
+ offline=False,
106
+ all_attributes_private=False,
107
+ private_attributes={'email', 'ssn'},
108
+ flush_interval=5,
109
+ poll_interval=30,
110
+ diagnostic_opt_out=False,
111
+ wrapper_name='my-wrapper',
112
+ wrapper_version='1.0.0'
113
+ )
114
+
115
+ ldclient.set_config(config)
116
+ client = ldclient.get()
117
+ ```
118
+
119
+ ### With Observability Plugin
120
+
121
+ ```python
122
+ import ldclient
123
+ from ldclient.config import Config
124
+ from ldobserve import ObservabilityPlugin
125
+ import os
126
+
127
+ config = Config(
128
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
129
+ plugins=[ObservabilityPlugin()]
130
+ )
131
+
132
+ ldclient.set_config(config)
133
+ client = ldclient.get()
134
+ ```
135
+
136
+ ### Singleton Pattern
137
+
138
+ **Critical:** The `ldclient` module maintains a singleton. Use `ldclient.get()` to access the shared instance.
139
+
140
+ ```python
141
+ # Initialize once at application startup
142
+ import ldclient
143
+ from ldclient.config import Config
144
+ import os
145
+
146
+ def initialize_launchdarkly():
147
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
148
+ ldclient.set_config(config)
149
+
150
+ client = ldclient.get()
151
+ if not client.wait_for_initialization(10):
152
+ raise Exception('LaunchDarkly failed to initialize')
153
+
154
+ return client
155
+
156
+ # Call once at startup
157
+ client = initialize_launchdarkly()
158
+
159
+ # Access anywhere in application
160
+ def get_ld_client():
161
+ return ldclient.get()
162
+ ```
163
+
164
+ ### Error Handling During Initialization
165
+
166
+ ```python
167
+ import ldclient
168
+ from ldclient.config import Config
169
+ import os
170
+
171
+ try:
172
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
173
+ ldclient.set_config(config)
174
+ client = ldclient.get()
175
+
176
+ if client.wait_for_initialization(10):
177
+ print('Client initialized successfully')
178
+ else:
179
+ print('Client initialization timeout - will use default values')
180
+ except Exception as e:
181
+ print(f'Initialization error: {e}')
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Contexts
187
+
188
+ ### Simple User Context
189
+
190
+ ```python
191
+ from ldclient import Context
192
+
193
+ context = Context.builder('user-key-123abc') \
194
+ .name('Sandy Smith') \
195
+ .build()
196
+ ```
197
+
198
+ ### Context with Custom Attributes
199
+
200
+ ```python
201
+ from ldclient import Context
202
+
203
+ context = Context.builder('user-key-123abc') \
204
+ .name('Sandy Smith') \
205
+ .set('email', 'sandy@example.com') \
206
+ .set('plan', 'premium') \
207
+ .set('betaTester', True) \
208
+ .build()
209
+ ```
210
+
211
+ ### Simple Context (Shorthand)
212
+
213
+ ```python
214
+ from ldclient import Context
215
+
216
+ # Just a key (defaults to 'user' kind)
217
+ context = Context.create('user-key-123abc')
218
+
219
+ # With kind specified
220
+ context = Context.create('org-key-456', 'organization')
221
+ ```
222
+
223
+ ### Anonymous Context
224
+
225
+ ```python
226
+ from ldclient import Context
227
+
228
+ context = Context.builder('anonymous-123') \
229
+ .anonymous(True) \
230
+ .build()
231
+ ```
232
+
233
+ ### Multi-Context (Multiple Kinds)
234
+
235
+ ```python
236
+ from ldclient import Context
237
+
238
+ user_context = Context.builder('user-key-123') \
239
+ .kind('user') \
240
+ .name('Sandy') \
241
+ .build()
242
+
243
+ org_context = Context.builder('org-key-456') \
244
+ .kind('organization') \
245
+ .name('Acme Corp') \
246
+ .build()
247
+
248
+ device_context = Context.builder('device-key-789') \
249
+ .kind('device') \
250
+ .set('platform', 'iOS') \
251
+ .build()
252
+
253
+ multi_context = Context.create_multi(user_context, org_context, device_context)
254
+ ```
255
+
256
+ ### Private Attributes
257
+
258
+ Mark specific attributes as private (not sent to LaunchDarkly):
259
+
260
+ ```python
261
+ from ldclient import Context
262
+
263
+ context = Context.builder('user-key-123abc') \
264
+ .name('Sandy Smith') \
265
+ .set('email', 'sandy@example.com') \
266
+ .set('ssn', '123-45-6789') \
267
+ .private('email', 'ssn') \
268
+ .build()
269
+ ```
270
+
271
+ ### Context from Dictionary
272
+
273
+ ```python
274
+ from ldclient import Context
275
+
276
+ context_dict = {
277
+ 'kind': 'user',
278
+ 'key': 'user-key-123abc',
279
+ 'name': 'Sandy Smith',
280
+ 'email': 'sandy@example.com'
281
+ }
282
+
283
+ context = Context.from_dict(context_dict)
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Flag Evaluation
289
+
290
+ ### Boolean Flag
291
+
292
+ ```python
293
+ import ldclient
294
+ from ldclient import Context
295
+
296
+ client = ldclient.get()
297
+ context = Context.create('user-key-123abc')
298
+
299
+ show_feature = client.variation('flag-key-123abc', context, False)
300
+
301
+ if show_feature:
302
+ print('Feature enabled')
303
+ else:
304
+ print('Feature disabled')
305
+ ```
306
+
307
+ ### String Flag
308
+
309
+ ```python
310
+ import ldclient
311
+ from ldclient import Context
312
+
313
+ client = ldclient.get()
314
+ context = Context.create('user-key-123abc')
315
+
316
+ theme = client.variation('theme-flag', context, 'light')
317
+
318
+ print(f'User theme: {theme}') # 'dark' or 'light'
319
+ ```
320
+
321
+ ### Number Flag
322
+
323
+ ```python
324
+ import ldclient
325
+ from ldclient import Context
326
+
327
+ client = ldclient.get()
328
+ context = Context.create('user-key-123abc')
329
+
330
+ max_items = client.variation('max-items', context, 10)
331
+
332
+ print(f'Max items: {max_items}') # e.g., 50
333
+ ```
334
+
335
+ ### JSON Flag
336
+
337
+ ```python
338
+ import ldclient
339
+ from ldclient import Context
340
+
341
+ client = ldclient.get()
342
+ context = Context.create('user-key-123abc')
343
+
344
+ config = client.variation('config-flag', context, {
345
+ 'timeout': 30,
346
+ 'retries': 3
347
+ })
348
+
349
+ print(f"Timeout: {config['timeout']}, Retries: {config['retries']}")
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Flag Evaluation with Details
355
+
356
+ ### Get Evaluation Reason
357
+
358
+ ```python
359
+ import ldclient
360
+ from ldclient import Context
361
+
362
+ client = ldclient.get()
363
+ context = Context.create('user-key-123abc')
364
+
365
+ result = client.variation_detail('flag-key-123abc', context, False)
366
+
367
+ print('Value:', result.value)
368
+ print('Variation Index:', result.variation_index)
369
+ print('Reason:', result.reason)
370
+ ```
371
+
372
+ ### Reason Object Structure
373
+
374
+ ```python
375
+ import ldclient
376
+ from ldclient import Context
377
+
378
+ client = ldclient.get()
379
+ context = Context.create('user-key-123abc')
380
+
381
+ result = client.variation_detail('flag-key-123abc', context, False)
382
+
383
+ # result.reason examples:
384
+ # {'kind': 'OFF'}
385
+ # {'kind': 'FALLTHROUGH'}
386
+ # {'kind': 'TARGET_MATCH'}
387
+ # {'kind': 'RULE_MATCH', 'ruleIndex': 0, 'ruleId': 'rule-id'}
388
+ # {'kind': 'PREREQUISITE_FAILED', 'prerequisiteKey': 'other-flag'}
389
+ # {'kind': 'ERROR', 'errorKind': 'MALFORMED_FLAG'}
390
+ ```
391
+
392
+ ### Using Evaluation Details for Debugging
393
+
394
+ ```python
395
+ import ldclient
396
+ from ldclient import Context
397
+
398
+ client = ldclient.get()
399
+ context = Context.create('user-key-123abc')
400
+
401
+ result = client.variation_detail('experiment-flag', context, 'control')
402
+
403
+ if result.reason['kind'] == 'ERROR':
404
+ print(f"Flag evaluation error: {result.reason['errorKind']}")
405
+ elif result.reason['kind'] == 'RULE_MATCH':
406
+ print(f"Matched rule: {result.reason['ruleIndex']}")
407
+ elif result.reason['kind'] == 'FALLTHROUGH':
408
+ print('Using fallthrough variation')
409
+
410
+ print(f'Serving variation: {result.value}')
411
+ ```
412
+
413
+ ---
414
+
415
+ ## All Flags State
416
+
417
+ ### Get All Flags for a Context
418
+
419
+ ```python
420
+ import ldclient
421
+ from ldclient import Context
422
+
423
+ client = ldclient.get()
424
+ context = Context.create('user-key-123abc')
425
+
426
+ state = client.all_flags_state(context)
427
+
428
+ if state.valid:
429
+ all_flags = state.to_values_map()
430
+ print('All flags:', all_flags)
431
+ # {'flag-1': True, 'flag-2': 'value', 'flag-3': 42}
432
+ ```
433
+
434
+ ### For Client-Side Bootstrapping
435
+
436
+ ```python
437
+ import ldclient
438
+ from ldclient import Context
439
+
440
+ client = ldclient.get()
441
+ context = Context.create('user-key-123abc')
442
+
443
+ state = client.all_flags_state(
444
+ context,
445
+ client_side_only=True,
446
+ with_reasons=True,
447
+ details_only_for_tracked_flags=False
448
+ )
449
+
450
+ if state.valid:
451
+ bootstrap_data = state.to_json_dict()
452
+ # Send to client-side
453
+ return {'flags': bootstrap_data}
454
+ ```
455
+
456
+ ### Get Individual Flag from State
457
+
458
+ ```python
459
+ import ldclient
460
+ from ldclient import Context
461
+
462
+ client = ldclient.get()
463
+ context = Context.create('user-key-123abc')
464
+
465
+ state = client.all_flags_state(context)
466
+
467
+ flag_value = state.get_flag_value('flag-key-123abc')
468
+ flag_reason = state.get_flag_reason('flag-key-123abc')
469
+
470
+ print(f'Value: {flag_value}')
471
+ print(f'Reason: {flag_reason}')
472
+ ```
473
+
474
+ ---
475
+
476
+ ## Event Tracking
477
+
478
+ ### Track Custom Event
479
+
480
+ ```python
481
+ import ldclient
482
+ from ldclient import Context
483
+
484
+ client = ldclient.get()
485
+ context = Context.create('user-key-123abc')
486
+
487
+ client.track('button-clicked', context)
488
+ ```
489
+
490
+ ### Track Event with Data
491
+
492
+ ```python
493
+ import ldclient
494
+ from ldclient import Context
495
+
496
+ client = ldclient.get()
497
+ context = Context.create('user-key-123abc')
498
+
499
+ client.track('purchase-completed', context, data={
500
+ 'item_id': 'item-123',
501
+ 'price': 29.99,
502
+ 'currency': 'USD'
503
+ })
504
+ ```
505
+
506
+ ### Track Event with Numeric Metric
507
+
508
+ ```python
509
+ import ldclient
510
+ from ldclient import Context
511
+
512
+ client = ldclient.get()
513
+ context = Context.create('user-key-123abc')
514
+
515
+ client.track('purchase-completed', context, data={
516
+ 'item_id': 'item-123'
517
+ }, metric_value=29.99)
518
+ ```
519
+
520
+ ### Identify Context
521
+
522
+ Send context attributes to LaunchDarkly for targeting:
523
+
524
+ ```python
525
+ import ldclient
526
+ from ldclient import Context
527
+
528
+ client = ldclient.get()
529
+
530
+ context = Context.builder('user-key-123abc') \
531
+ .name('Sandy Smith') \
532
+ .set('email', 'sandy@example.com') \
533
+ .build()
534
+
535
+ client.identify(context)
536
+ ```
537
+
538
+ ### Flush Events
539
+
540
+ Force immediate delivery of pending events:
541
+
542
+ ```python
543
+ import ldclient
544
+
545
+ client = ldclient.get()
546
+ client.flush()
547
+ ```
548
+
549
+ ### Auto-Flush on Shutdown
550
+
551
+ ```python
552
+ import ldclient
553
+ import signal
554
+ import sys
555
+
556
+ def shutdown_handler(signum, frame):
557
+ print('Shutting down...')
558
+ client = ldclient.get()
559
+ client.flush()
560
+ client.close()
561
+ sys.exit(0)
562
+
563
+ signal.signal(signal.SIGTERM, shutdown_handler)
564
+ signal.signal(signal.SIGINT, shutdown_handler)
565
+ ```
566
+
567
+ ---
568
+
569
+ ## Data Modes
570
+
571
+ ### Streaming Mode (Default)
572
+
573
+ ```python
574
+ from ldclient.config import Config
575
+ import ldclient
576
+ import os
577
+
578
+ config = Config(
579
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
580
+ stream=True
581
+ )
582
+
583
+ ldclient.set_config(config)
584
+ ```
585
+
586
+ ### Polling Mode
587
+
588
+ ```python
589
+ from ldclient.config import Config
590
+ import ldclient
591
+ import os
592
+
593
+ config = Config(
594
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
595
+ stream=False,
596
+ poll_interval=60 # seconds
597
+ )
598
+
599
+ ldclient.set_config(config)
600
+ ```
601
+
602
+ ### Offline Mode
603
+
604
+ ```python
605
+ from ldclient.config import Config
606
+ import ldclient
607
+ import os
608
+
609
+ config = Config(
610
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
611
+ offline=True
612
+ )
613
+
614
+ ldclient.set_config(config)
615
+ client = ldclient.get()
616
+
617
+ # All flags return default values
618
+ result = client.variation('flag-key', context, False)
619
+ # Will always return False (the default)
620
+ ```
621
+
622
+ ---
623
+
624
+ ## HTTP Configuration
625
+
626
+ ### Configure HTTP Timeouts
627
+
628
+ ```python
629
+ from ldclient.config import Config, HTTPConfig
630
+ import ldclient
631
+ import os
632
+
633
+ http_config = HTTPConfig(
634
+ connect_timeout=10,
635
+ read_timeout=15
636
+ )
637
+
638
+ config = Config(
639
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
640
+ http=http_config
641
+ )
642
+
643
+ ldclient.set_config(config)
644
+ ```
645
+
646
+ ### Configure HTTP Proxy
647
+
648
+ ```python
649
+ from ldclient.config import Config, HTTPConfig
650
+ import ldclient
651
+ import os
652
+
653
+ http_config = HTTPConfig(
654
+ http_proxy='http://proxy.example.com:8080'
655
+ )
656
+
657
+ config = Config(
658
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
659
+ http=http_config
660
+ )
661
+
662
+ ldclient.set_config(config)
663
+ ```
664
+
665
+ ### Environment Variable Proxy
666
+
667
+ ```python
668
+ import os
669
+
670
+ os.environ['HTTPS_PROXY'] = 'https://proxy.example.com:8080'
671
+
672
+ # SDK will automatically use the proxy
673
+ ```
674
+
675
+ ### Custom CA Certificates
676
+
677
+ ```python
678
+ from ldclient.config import Config, HTTPConfig
679
+ import ldclient
680
+ import os
681
+
682
+ http_config = HTTPConfig(
683
+ ca_certs='/path/to/ca-bundle.crt'
684
+ )
685
+
686
+ config = Config(
687
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
688
+ http=http_config
689
+ )
690
+
691
+ ldclient.set_config(config)
692
+ ```
693
+
694
+ ### Disable SSL Verification (Not Recommended)
695
+
696
+ ```python
697
+ from ldclient.config import Config, HTTPConfig
698
+ import ldclient
699
+ import os
700
+
701
+ http_config = HTTPConfig(
702
+ disable_ssl_verification=True
703
+ )
704
+
705
+ config = Config(
706
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
707
+ http=http_config
708
+ )
709
+
710
+ ldclient.set_config(config)
711
+ ```
712
+
713
+ ---
714
+
715
+ ## Relay Proxy
716
+
717
+ ### Using LaunchDarkly Relay Proxy
718
+
719
+ ```python
720
+ from ldclient.config import Config
721
+ import ldclient
722
+ import os
723
+
724
+ config = Config(
725
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
726
+ base_uri='http://relay-proxy.example.com',
727
+ stream_uri='http://relay-proxy.example.com',
728
+ events_uri='http://relay-proxy.example.com'
729
+ )
730
+
731
+ ldclient.set_config(config)
732
+ ```
733
+
734
+ ---
735
+
736
+ ## Feature Stores
737
+
738
+ ### Redis Feature Store
739
+
740
+ ```python
741
+ from ldclient.config import Config
742
+ from ldclient.integrations import Redis
743
+ import ldclient
744
+ import os
745
+
746
+ redis_store = Redis.new_feature_store(
747
+ url='redis://localhost:6379',
748
+ prefix='ld',
749
+ max_connections=16,
750
+ expiration=30
751
+ )
752
+
753
+ config = Config(
754
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
755
+ feature_store=redis_store
756
+ )
757
+
758
+ ldclient.set_config(config)
759
+ ```
760
+
761
+ ### DynamoDB Feature Store
762
+
763
+ ```python
764
+ from ldclient.config import Config
765
+ from ldclient.integrations import DynamoDB
766
+ import ldclient
767
+ import os
768
+
769
+ dynamodb_store = DynamoDB.new_feature_store(
770
+ 'feature-flags-table',
771
+ prefix='ld',
772
+ expiration=30
773
+ )
774
+
775
+ config = Config(
776
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
777
+ feature_store=dynamodb_store
778
+ )
779
+
780
+ ldclient.set_config(config)
781
+ ```
782
+
783
+ ### Consul Feature Store
784
+
785
+ ```python
786
+ from ldclient.config import Config
787
+ from ldclient.integrations import Consul
788
+ import ldclient
789
+ import os
790
+
791
+ consul_store = Consul.new_feature_store(
792
+ host='localhost',
793
+ port=8500,
794
+ prefix='ld',
795
+ expiration=30
796
+ )
797
+
798
+ config = Config(
799
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
800
+ feature_store=consul_store
801
+ )
802
+
803
+ ldclient.set_config(config)
804
+ ```
805
+
806
+ ---
807
+
808
+ ## File-Based Data Source
809
+
810
+ ### Load Flags from File
811
+
812
+ ```python
813
+ from ldclient.config import Config
814
+ from ldclient.integrations import Files
815
+ import ldclient
816
+ import os
817
+
818
+ file_source = Files.new_data_source(
819
+ paths=['./flags.json'],
820
+ auto_update=True,
821
+ poll_interval=1
822
+ )
823
+
824
+ config = Config(
825
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
826
+ update_processor_class=file_source
827
+ )
828
+
829
+ ldclient.set_config(config)
830
+ ```
831
+
832
+ ### flags.json Format
833
+
834
+ ```json
835
+ {
836
+ "flags": {
837
+ "flag-key-123abc": {
838
+ "key": "flag-key-123abc",
839
+ "on": true,
840
+ "variations": [true, false],
841
+ "fallthrough": {
842
+ "variation": 0
843
+ }
844
+ }
845
+ },
846
+ "segments": {}
847
+ }
848
+ ```
849
+
850
+ ---
851
+
852
+ ## Big Segments
853
+
854
+ ### Configure Big Segments Store
855
+
856
+ ```python
857
+ from ldclient.config import Config, BigSegmentsConfig
858
+ from ldclient.integrations import Redis
859
+ import ldclient
860
+ import os
861
+
862
+ redis_store = Redis.new_big_segment_store(
863
+ url='redis://localhost:6379',
864
+ prefix='big-segments'
865
+ )
866
+
867
+ big_segments = BigSegmentsConfig(
868
+ store=redis_store,
869
+ context_cache_size=1000,
870
+ context_cache_time=5,
871
+ status_poll_interval=5,
872
+ stale_after=120
873
+ )
874
+
875
+ config = Config(
876
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
877
+ big_segments=big_segments
878
+ )
879
+
880
+ ldclient.set_config(config)
881
+ ```
882
+
883
+ ### Check Big Segments Status
884
+
885
+ ```python
886
+ import ldclient
887
+ from ldclient import Context
888
+
889
+ client = ldclient.get()
890
+ context = Context.create('user-key-123abc')
891
+
892
+ result = client.variation_detail('segment-flag', context, False)
893
+
894
+ if result.reason.get('bigSegmentsStatus') == 'STALE':
895
+ print('Big segments data is stale')
896
+ ```
897
+
898
+ ---
899
+
900
+ ## Hooks
901
+
902
+ ### Create a Hook
903
+
904
+ ```python
905
+ from ldclient.hook import Hook, Metadata
906
+
907
+ class LoggingHook(Hook):
908
+ @property
909
+ def metadata(self):
910
+ return Metadata(name='logging-hook')
911
+
912
+ def before_evaluation(self, series_context, data):
913
+ print(f'Evaluating flag: {series_context.flag_key}')
914
+ return data
915
+
916
+ def after_evaluation(self, series_context, data, detail):
917
+ print(f'Flag result: {detail.value}')
918
+ return data
919
+ ```
920
+
921
+ ### Register Hook via Configuration
922
+
923
+ ```python
924
+ from ldclient.config import Config
925
+ import ldclient
926
+ import os
927
+
928
+ config = Config(
929
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
930
+ hooks=[LoggingHook()]
931
+ )
932
+
933
+ ldclient.set_config(config)
934
+ ```
935
+
936
+ ### Add Hook at Runtime
937
+
938
+ ```python
939
+ import ldclient
940
+
941
+ client = ldclient.get()
942
+ hook = LoggingHook()
943
+ client.add_hook(hook)
944
+ ```
945
+
946
+ ---
947
+
948
+ ## Migrations
949
+
950
+ ### Create Migration
951
+
952
+ ```python
953
+ import ldclient
954
+ from ldclient.migrations import MigratorBuilder, Stage, ExecutionOrder
955
+
956
+ client = ldclient.get()
957
+
958
+ def old_read(payload):
959
+ return old_database.read(payload['id'])
960
+
961
+ def new_read(payload):
962
+ return new_database.read(payload['id'])
963
+
964
+ def old_write(payload):
965
+ old_database.write(payload['id'], payload['data'])
966
+
967
+ def new_write(payload):
968
+ new_database.write(payload['id'], payload['data'])
969
+
970
+ def check_consistency(old_value, new_value):
971
+ return old_value == new_value
972
+
973
+ migrator = MigratorBuilder(client) \
974
+ .read(old_read, new_read, check_consistency) \
975
+ .write(old_write, new_write) \
976
+ .read_execution_order(ExecutionOrder.PARALLEL) \
977
+ .track_latency(True) \
978
+ .track_errors(True) \
979
+ .build()
980
+ ```
981
+
982
+ ### Execute Migration Read
983
+
984
+ ```python
985
+ import ldclient
986
+ from ldclient import Context
987
+ from ldclient.migrations import Stage
988
+
989
+ client = ldclient.get()
990
+ context = Context.create('user-key-123abc')
991
+
992
+ stage_str = client.variation('migration-flag', context, 'off')
993
+ stage = Stage.from_str(stage_str)
994
+
995
+ result = migrator.read(
996
+ 'migration-flag',
997
+ context,
998
+ stage,
999
+ payload={'id': 'record-123'}
1000
+ )
1001
+
1002
+ if result.is_success():
1003
+ print('Data:', result.value)
1004
+ else:
1005
+ print('Migration read failed')
1006
+ ```
1007
+
1008
+ ### Execute Migration Write
1009
+
1010
+ ```python
1011
+ import ldclient
1012
+ from ldclient import Context
1013
+ from ldclient.migrations import Stage
1014
+
1015
+ client = ldclient.get()
1016
+ context = Context.create('user-key-123abc')
1017
+
1018
+ stage_str = client.variation('migration-flag', context, 'off')
1019
+ stage = Stage.from_str(stage_str)
1020
+
1021
+ result = migrator.write(
1022
+ 'migration-flag',
1023
+ context,
1024
+ stage,
1025
+ payload={'id': 'record-123', 'data': {'name': 'Example'}}
1026
+ )
1027
+
1028
+ if result.is_success():
1029
+ print('Write completed')
1030
+ ```
1031
+
1032
+ ### Migration Stages
1033
+
1034
+ ```python
1035
+ from ldclient.migrations import Stage
1036
+
1037
+ # Available stages:
1038
+ Stage.OFF # Use old implementation only
1039
+ Stage.DUALWRITE # Write to both, read from old
1040
+ Stage.SHADOW # Read from both, write to both, use old for responses
1041
+ Stage.LIVE # Read from both, write to both, use new for responses
1042
+ Stage.RAMPDOWN # Write to both, read from new
1043
+ Stage.COMPLETE # Use new implementation only
1044
+ ```
1045
+
1046
+ ### Execution Order
1047
+
1048
+ ```python
1049
+ from ldclient.migrations import ExecutionOrder
1050
+
1051
+ ExecutionOrder.SERIAL # Execute old then new sequentially
1052
+ ExecutionOrder.RANDOM # Randomize execution order
1053
+ ExecutionOrder.PARALLEL # Execute both concurrently
1054
+ ```
1055
+
1056
+ ---
1057
+
1058
+ ## Worker-Based Servers (uWSGI, Gunicorn)
1059
+
1060
+ ### uWSGI Configuration
1061
+
1062
+ ```python
1063
+ import ldclient
1064
+ from ldclient.config import Config
1065
+ import os
1066
+
1067
+ # Initialize before forking
1068
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1069
+ ldclient.set_config(config)
1070
+ client = ldclient.get()
1071
+
1072
+ # Reinitialize after fork
1073
+ try:
1074
+ import uwsgidecorators
1075
+
1076
+ @uwsgidecorators.postfork
1077
+ def reinit_ld_after_fork():
1078
+ ldclient.get().postfork()
1079
+ print('LaunchDarkly reinitialized after fork')
1080
+ except ImportError:
1081
+ pass
1082
+ ```
1083
+
1084
+ ### Gunicorn Configuration
1085
+
1086
+ ```python
1087
+ # gunicorn_config.py
1088
+ import ldclient
1089
+ from ldclient.config import Config
1090
+ import os
1091
+
1092
+ def on_starting(server):
1093
+ """Called just before the master process is initialized."""
1094
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1095
+ ldclient.set_config(config)
1096
+
1097
+ def post_fork(server, worker):
1098
+ """Called just after a worker has been forked."""
1099
+ ldclient.get().postfork()
1100
+ ```
1101
+
1102
+ ### Without Decorator
1103
+
1104
+ ```python
1105
+ import ldclient
1106
+ from ldclient.config import Config
1107
+ import os
1108
+
1109
+ # After forking in worker process
1110
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1111
+ ldclient.set_config(config)
1112
+ client = ldclient.get()
1113
+
1114
+ # Reinitialize
1115
+ client.postfork()
1116
+ ```
1117
+
1118
+ ---
1119
+
1120
+ ## Application Metadata
1121
+
1122
+ ### Set Application Info
1123
+
1124
+ ```python
1125
+ from ldclient.config import Config
1126
+ import ldclient
1127
+ import os
1128
+
1129
+ config = Config(
1130
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
1131
+ application={
1132
+ 'id': 'my-app',
1133
+ 'version': '1.2.3',
1134
+ 'name': 'My Application',
1135
+ 'version_name': 'v1.2.3-beta'
1136
+ }
1137
+ )
1138
+
1139
+ ldclient.set_config(config)
1140
+ ```
1141
+
1142
+ ---
1143
+
1144
+ ## Secure Mode
1145
+
1146
+ ### Generate Secure Mode Hash
1147
+
1148
+ ```python
1149
+ import hashlib
1150
+ import hmac
1151
+ import os
1152
+
1153
+ def generate_secure_mode_hash(sdk_key, context_key):
1154
+ """Generate secure mode hash for client-side SDK."""
1155
+ return hmac.new(
1156
+ sdk_key.encode(),
1157
+ context_key.encode(),
1158
+ hashlib.sha256
1159
+ ).hexdigest()
1160
+
1161
+ hash_value = generate_secure_mode_hash(
1162
+ os.environ['LAUNCHDARKLY_SDK_KEY'],
1163
+ 'user-key-123abc'
1164
+ )
1165
+
1166
+ # Send hash to client-side
1167
+ return {'hash': hash_value}
1168
+ ```
1169
+
1170
+ ---
1171
+
1172
+ ## Logging
1173
+
1174
+ ### Configure Logger
1175
+
1176
+ ```python
1177
+ import logging
1178
+ from ldclient.config import Config
1179
+ import ldclient
1180
+ import os
1181
+
1182
+ # Set up logging
1183
+ logging.basicConfig(level=logging.INFO)
1184
+ logger = logging.getLogger('ldclient')
1185
+ logger.setLevel(logging.DEBUG)
1186
+
1187
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1188
+ ldclient.set_config(config)
1189
+ ```
1190
+
1191
+ ### Custom Log Level
1192
+
1193
+ ```python
1194
+ import logging
1195
+ from ldclient.config import Config
1196
+ import ldclient
1197
+ import os
1198
+
1199
+ # Create custom logger
1200
+ ld_logger = logging.getLogger('ldclient')
1201
+ ld_logger.setLevel(logging.WARNING)
1202
+
1203
+ handler = logging.StreamHandler()
1204
+ handler.setLevel(logging.WARNING)
1205
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
1206
+ handler.setFormatter(formatter)
1207
+ ld_logger.addHandler(handler)
1208
+
1209
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1210
+ ldclient.set_config(config)
1211
+ ```
1212
+
1213
+ ---
1214
+
1215
+ ## Private Attributes Configuration
1216
+
1217
+ ### Global Private Attributes
1218
+
1219
+ ```python
1220
+ from ldclient.config import Config
1221
+ import ldclient
1222
+ import os
1223
+
1224
+ config = Config(
1225
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
1226
+ private_attributes={'email', 'ssn', 'address'}
1227
+ )
1228
+
1229
+ ldclient.set_config(config)
1230
+ ```
1231
+
1232
+ ### All Attributes Private
1233
+
1234
+ ```python
1235
+ from ldclient.config import Config
1236
+ import ldclient
1237
+ import os
1238
+
1239
+ config = Config(
1240
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
1241
+ all_attributes_private=True
1242
+ )
1243
+
1244
+ ldclient.set_config(config)
1245
+ ```
1246
+
1247
+ ---
1248
+
1249
+ ## Diagnostics
1250
+
1251
+ ### Disable Diagnostic Events
1252
+
1253
+ ```python
1254
+ from ldclient.config import Config
1255
+ import ldclient
1256
+ import os
1257
+
1258
+ config = Config(
1259
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
1260
+ diagnostic_opt_out=True
1261
+ )
1262
+
1263
+ ldclient.set_config(config)
1264
+ ```
1265
+
1266
+ ---
1267
+
1268
+ ## Testing
1269
+
1270
+ ### Using File Data Source for Tests
1271
+
1272
+ ```python
1273
+ from ldclient.config import Config
1274
+ from ldclient.integrations import Files
1275
+ import ldclient
1276
+
1277
+ # Create test flags file
1278
+ test_flags = {
1279
+ 'flags': {
1280
+ 'test-flag': {
1281
+ 'key': 'test-flag',
1282
+ 'on': True,
1283
+ 'variations': [True, False],
1284
+ 'fallthrough': {'variation': 0}
1285
+ }
1286
+ },
1287
+ 'segments': {}
1288
+ }
1289
+
1290
+ # Write to file
1291
+ import json
1292
+ with open('test-flags.json', 'w') as f:
1293
+ json.dump(test_flags, f)
1294
+
1295
+ # Configure client with file source
1296
+ file_source = Files.new_data_source(paths=['test-flags.json'])
1297
+
1298
+ config = Config(
1299
+ sdk_key='test-sdk-key',
1300
+ update_processor_class=file_source,
1301
+ send_events=False
1302
+ )
1303
+
1304
+ ldclient.set_config(config)
1305
+ client = ldclient.get()
1306
+ ```
1307
+
1308
+ ### Mock Client for Unit Tests
1309
+
1310
+ ```python
1311
+ from unittest.mock import Mock, MagicMock
1312
+ from ldclient import EvaluationDetail
1313
+
1314
+ mock_client = Mock()
1315
+ mock_client.variation = Mock(return_value=True)
1316
+ mock_client.variation_detail = Mock(return_value=EvaluationDetail(
1317
+ value=True,
1318
+ variation_index=0,
1319
+ reason={'kind': 'OFF'}
1320
+ ))
1321
+ mock_client.track = Mock()
1322
+ mock_client.identify = Mock()
1323
+ mock_client.flush = Mock()
1324
+ mock_client.close = Mock()
1325
+ ```
1326
+
1327
+ ---
1328
+
1329
+ ## Shutdown and Cleanup
1330
+
1331
+ ### Graceful Shutdown
1332
+
1333
+ ```python
1334
+ import ldclient
1335
+
1336
+ def shutdown():
1337
+ print('Flushing events...')
1338
+ client = ldclient.get()
1339
+ client.flush()
1340
+
1341
+ print('Closing client...')
1342
+ client.close()
1343
+
1344
+ print('Shutdown complete')
1345
+ ```
1346
+
1347
+ ### Check Initialization Status
1348
+
1349
+ ```python
1350
+ import ldclient
1351
+
1352
+ client = ldclient.get()
1353
+
1354
+ if client.is_initialized():
1355
+ print('Client is ready')
1356
+ else:
1357
+ print('Client is not initialized yet')
1358
+ ```
1359
+
1360
+ ---
1361
+
1362
+ ## Complete Flask Example
1363
+
1364
+ ```python
1365
+ from flask import Flask, request, jsonify
1366
+ import ldclient
1367
+ from ldclient.config import Config
1368
+ from ldclient import Context
1369
+ import os
1370
+ import signal
1371
+ import sys
1372
+
1373
+ app = Flask(__name__)
1374
+
1375
+ # Initialize LaunchDarkly
1376
+ def init_launchdarkly():
1377
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1378
+ ldclient.set_config(config)
1379
+
1380
+ client = ldclient.get()
1381
+ if not client.wait_for_initialization(10):
1382
+ print('LaunchDarkly initialization timeout')
1383
+ else:
1384
+ print('LaunchDarkly initialized')
1385
+
1386
+ return client
1387
+
1388
+ # Route using feature flag
1389
+ @app.route('/feature')
1390
+ def feature():
1391
+ client = ldclient.get()
1392
+
1393
+ user_id = request.headers.get('X-User-ID', 'anonymous')
1394
+ user_email = request.headers.get('X-User-Email')
1395
+
1396
+ context = Context.builder(user_id) \
1397
+ .set('email', user_email) \
1398
+ .build()
1399
+
1400
+ show_new_feature = client.variation('new-feature-flag', context, False)
1401
+
1402
+ if show_new_feature:
1403
+ return jsonify({
1404
+ 'feature': 'new',
1405
+ 'message': 'Welcome to the new feature!'
1406
+ })
1407
+ else:
1408
+ return jsonify({
1409
+ 'feature': 'old',
1410
+ 'message': 'Using legacy feature'
1411
+ })
1412
+
1413
+ # Track custom event
1414
+ @app.route('/track-event', methods=['POST'])
1415
+ def track_event():
1416
+ client = ldclient.get()
1417
+
1418
+ data = request.get_json()
1419
+ user_id = data.get('userId')
1420
+
1421
+ context = Context.create(user_id)
1422
+
1423
+ client.track('button-clicked', context, data={
1424
+ 'button_id': data.get('buttonId')
1425
+ })
1426
+
1427
+ return jsonify({'success': True})
1428
+
1429
+ # Graceful shutdown
1430
+ def shutdown_handler(signum, frame):
1431
+ print('Shutting down...')
1432
+ client = ldclient.get()
1433
+ client.flush()
1434
+ client.close()
1435
+ sys.exit(0)
1436
+
1437
+ signal.signal(signal.SIGTERM, shutdown_handler)
1438
+ signal.signal(signal.SIGINT, shutdown_handler)
1439
+
1440
+ if __name__ == '__main__':
1441
+ init_launchdarkly()
1442
+ app.run(port=5000)
1443
+ ```
1444
+
1445
+ ---
1446
+
1447
+ ## Complete Django Example
1448
+
1449
+ ```python
1450
+ # settings.py
1451
+ import ldclient
1452
+ from ldclient.config import Config
1453
+ import os
1454
+
1455
+ # Initialize LaunchDarkly
1456
+ def init_launchdarkly():
1457
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1458
+ ldclient.set_config(config)
1459
+
1460
+ client = ldclient.get()
1461
+ if client.wait_for_initialization(10):
1462
+ print('LaunchDarkly initialized')
1463
+
1464
+ return client
1465
+
1466
+ LAUNCHDARKLY_CLIENT = init_launchdarkly()
1467
+
1468
+
1469
+ # views.py
1470
+ from django.http import JsonResponse
1471
+ from ldclient import Context
1472
+ import ldclient
1473
+
1474
+ def feature_view(request):
1475
+ client = ldclient.get()
1476
+
1477
+ user_id = request.META.get('HTTP_X_USER_ID', 'anonymous')
1478
+ user_email = request.META.get('HTTP_X_USER_EMAIL')
1479
+
1480
+ context = Context.builder(user_id) \
1481
+ .set('email', user_email) \
1482
+ .build()
1483
+
1484
+ show_new_feature = client.variation('new-feature-flag', context, False)
1485
+
1486
+ if show_new_feature:
1487
+ return JsonResponse({
1488
+ 'feature': 'new',
1489
+ 'message': 'Welcome to the new feature!'
1490
+ })
1491
+ else:
1492
+ return JsonResponse({
1493
+ 'feature': 'old',
1494
+ 'message': 'Using legacy feature'
1495
+ })
1496
+
1497
+
1498
+ # apps.py (for cleanup on shutdown)
1499
+ from django.apps import AppConfig
1500
+ import ldclient
1501
+
1502
+ class MyAppConfig(AppConfig):
1503
+ name = 'myapp'
1504
+
1505
+ def ready(self):
1506
+ import signal
1507
+ import sys
1508
+
1509
+ def shutdown_handler(signum, frame):
1510
+ client = ldclient.get()
1511
+ client.flush()
1512
+ client.close()
1513
+ sys.exit(0)
1514
+
1515
+ signal.signal(signal.SIGTERM, shutdown_handler)
1516
+ ```
1517
+
1518
+ ---
1519
+
1520
+ ## Complete FastAPI Example
1521
+
1522
+ ```python
1523
+ from fastapi import FastAPI, Header
1524
+ from pydantic import BaseModel
1525
+ import ldclient
1526
+ from ldclient.config import Config
1527
+ from ldclient import Context
1528
+ import os
1529
+
1530
+ app = FastAPI()
1531
+
1532
+ # Initialize LaunchDarkly
1533
+ @app.on_event("startup")
1534
+ async def startup_event():
1535
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1536
+ ldclient.set_config(config)
1537
+
1538
+ client = ldclient.get()
1539
+ if not client.wait_for_initialization(10):
1540
+ print('LaunchDarkly initialization timeout')
1541
+ else:
1542
+ print('LaunchDarkly initialized')
1543
+
1544
+ # Shutdown
1545
+ @app.on_event("shutdown")
1546
+ async def shutdown_event():
1547
+ client = ldclient.get()
1548
+ client.flush()
1549
+ client.close()
1550
+
1551
+ # Route using feature flag
1552
+ @app.get("/feature")
1553
+ async def get_feature(
1554
+ x_user_id: str = Header(default='anonymous'),
1555
+ x_user_email: str = Header(default=None)
1556
+ ):
1557
+ client = ldclient.get()
1558
+
1559
+ context = Context.builder(x_user_id) \
1560
+ .set('email', x_user_email) \
1561
+ .build()
1562
+
1563
+ show_new_feature = client.variation('new-feature-flag', context, False)
1564
+
1565
+ if show_new_feature:
1566
+ return {
1567
+ 'feature': 'new',
1568
+ 'message': 'Welcome to the new feature!'
1569
+ }
1570
+ else:
1571
+ return {
1572
+ 'feature': 'old',
1573
+ 'message': 'Using legacy feature'
1574
+ }
1575
+
1576
+ # Track custom event
1577
+ class TrackEventRequest(BaseModel):
1578
+ userId: str
1579
+ buttonId: str
1580
+
1581
+ @app.post("/track-event")
1582
+ async def track_event(event: TrackEventRequest):
1583
+ client = ldclient.get()
1584
+
1585
+ context = Context.create(event.userId)
1586
+
1587
+ client.track('button-clicked', context, data={
1588
+ 'button_id': event.buttonId
1589
+ })
1590
+
1591
+ return {'success': True}
1592
+ ```
1593
+
1594
+ ---
1595
+
1596
+ ## OpenTelemetry Integration
1597
+
1598
+ ### Setup OpenTelemetry with LaunchDarkly
1599
+
1600
+ ```python
1601
+ import ldclient
1602
+ from ldclient.config import Config
1603
+ from ldobserve import ObservabilityPlugin
1604
+ from opentelemetry import metrics
1605
+ from opentelemetry.sdk.metrics import MeterProvider
1606
+ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
1607
+ from opentelemetry.exporter.prometheus import PrometheusMetricReader
1608
+ import os
1609
+
1610
+ # Set up OpenTelemetry
1611
+ prometheus_reader = PrometheusMetricReader()
1612
+ meter_provider = MeterProvider(metric_readers=[prometheus_reader])
1613
+ metrics.set_meter_provider(meter_provider)
1614
+
1615
+ # Initialize LaunchDarkly with observability
1616
+ config = Config(
1617
+ sdk_key=os.environ['LAUNCHDARKLY_SDK_KEY'],
1618
+ plugins=[ObservabilityPlugin()]
1619
+ )
1620
+
1621
+ ldclient.set_config(config)
1622
+ client = ldclient.get()
1623
+ ```
1624
+
1625
+ ---
1626
+
1627
+ ## Error Handling
1628
+
1629
+ ### Handle Initialization Errors
1630
+
1631
+ ```python
1632
+ import ldclient
1633
+ from ldclient.config import Config
1634
+ import os
1635
+
1636
+ try:
1637
+ config = Config(os.environ['LAUNCHDARKLY_SDK_KEY'])
1638
+ ldclient.set_config(config)
1639
+ client = ldclient.get()
1640
+
1641
+ if not client.wait_for_initialization(10):
1642
+ print('Initialization timeout - using default values')
1643
+ except Exception as e:
1644
+ print(f'LaunchDarkly initialization error: {e}')
1645
+ ```
1646
+
1647
+ ### Handle Evaluation Errors
1648
+
1649
+ ```python
1650
+ import ldclient
1651
+ from ldclient import Context
1652
+
1653
+ client = ldclient.get()
1654
+ context = Context.create('user-key-123abc')
1655
+
1656
+ result = client.variation_detail('flag-key', context, False)
1657
+
1658
+ if result.reason['kind'] == 'ERROR':
1659
+ error_kind = result.reason['errorKind']
1660
+
1661
+ if error_kind == 'MALFORMED_FLAG':
1662
+ print('Flag configuration is invalid')
1663
+ elif error_kind == 'FLAG_NOT_FOUND':
1664
+ print('Flag does not exist')
1665
+ elif error_kind == 'USER_NOT_SPECIFIED':
1666
+ print('Context is invalid')
1667
+ elif error_kind == 'WRONG_TYPE':
1668
+ print('Flag type mismatch')
1669
+ else:
1670
+ print(f'Unknown error: {error_kind}')
1671
+ ```