kameleoon-client-python 3.20.0__tar.gz → 3.22.1__tar.gz

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 (207) hide show
  1. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/PKG-INFO +15 -15
  2. kameleoon_client_python-3.22.1/kameleoon/client_readiness/async_readiness.py +43 -0
  3. kameleoon_client_python-3.22.1/kameleoon/client_readiness/threading_readiness.py +134 -0
  4. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/visitor_manager.py +11 -13
  5. kameleoon_client_python-3.22.1/kameleoon/events/__init__.py +19 -0
  6. kameleoon_client_python-3.22.1/kameleoon/events/data_file_update_event.py +33 -0
  7. kameleoon_client_python-3.22.1/kameleoon/events/data_file_update_handler.py +23 -0
  8. kameleoon_client_python-3.22.1/kameleoon/events/event_handler.py +13 -0
  9. kameleoon_client_python-3.22.1/kameleoon/events/event_manager.py +62 -0
  10. kameleoon_client_python-3.22.1/kameleoon/events/event_type.py +70 -0
  11. kameleoon_client_python-3.22.1/kameleoon/events/http_request_failure.py +70 -0
  12. kameleoon_client_python-3.22.1/kameleoon/events/http_request_handler.py +43 -0
  13. kameleoon_client_python-3.22.1/kameleoon/events/request_type.py +21 -0
  14. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/exceptions.py +18 -0
  15. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/multi_threading.py +34 -14
  16. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/scheduler.py +17 -3
  17. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/string_utils.py +2 -3
  18. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/kameleoon_client.py +170 -48
  19. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/tracking/tracking_manager.py +60 -17
  20. kameleoon_client_python-3.22.1/kameleoon/managers/tracking/visitor_tracking_registry.py +112 -0
  21. kameleoon_client_python-3.22.1/kameleoon/network/access_token_source.py +175 -0
  22. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/net_provider.py +2 -1
  23. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/network_manager.py +6 -0
  24. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/network_manager_factory.py +2 -0
  25. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/network_manager_factory_impl.py +3 -2
  26. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/network_manager_impl.py +7 -0
  27. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/automation_service_impl.py +2 -1
  28. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/configuration_service_impl.py +4 -1
  29. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/data_service_impl.py +5 -3
  30. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/service_impl.py +46 -6
  31. kameleoon_client_python-3.20.0/kameleoon/real_time/real_time_configuration_service.py → kameleoon_client_python-3.22.1/kameleoon/real_time/real_time_event_service.py +27 -18
  32. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/sdk_version.py +1 -1
  33. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/unknown_condition.py +3 -3
  34. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon_client_python.egg-info/PKG-INFO +15 -15
  35. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon_client_python.egg-info/SOURCES.txt +11 -6
  36. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon_client_python.egg-info/requires.txt +14 -14
  37. kameleoon_client_python-3.22.1/tests/benchmarks/visitor_tracking_registry_benchmark.py +416 -0
  38. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/proj/settings.py +0 -2
  39. kameleoon_client_python-3.20.0/kameleoon/client_readiness/async_readiness.py +0 -48
  40. kameleoon_client_python-3.20.0/kameleoon/client_readiness/threading_readiness.py +0 -44
  41. kameleoon_client_python-3.20.0/kameleoon/managers/tracking/visitor_tracking_registry.py +0 -89
  42. kameleoon_client_python-3.20.0/kameleoon/network/access_token_source.py +0 -138
  43. kameleoon_client_python-3.20.0/kameleoon/storage/cache.py +0 -67
  44. kameleoon_client_python-3.20.0/kameleoon/storage/cache_factory.py +0 -17
  45. kameleoon_client_python-3.20.0/kameleoon/storage/cache_factory_impl.py +0 -12
  46. kameleoon_client_python-3.20.0/kameleoon/storage/cache_impl.py +0 -36
  47. kameleoon_client_python-3.20.0/tests/integration/proj/proj/__init__.py +0 -0
  48. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/MANIFEST.in +0 -0
  49. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/README.md +0 -0
  50. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/__init__.py +0 -0
  51. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/client_readiness/__init__.py +0 -0
  52. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/__init__.py +0 -0
  53. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/consent_blocking_behaviour.py +0 -0
  54. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/custom_data_info.py +0 -0
  55. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/data_file.py +0 -0
  56. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/experiment.py +0 -0
  57. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/feature_flag.py +0 -0
  58. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/me_group.py +0 -0
  59. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/rule.py +0 -0
  60. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/rule_type.py +0 -0
  61. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/settings.py +0 -0
  62. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/variable.py +0 -0
  63. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/variation.py +0 -0
  64. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/configuration/variation_by_exposition.py +0 -0
  65. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/__init__.py +0 -0
  66. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/application_version.py +0 -0
  67. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/browser.py +0 -0
  68. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/cbscores.py +0 -0
  69. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/conversion.py +0 -0
  70. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/cookie.py +0 -0
  71. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/custom_data.py +0 -0
  72. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/data.py +0 -0
  73. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/device.py +0 -0
  74. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/geolocation.py +0 -0
  75. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/kcs_heat.py +0 -0
  76. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/__init__.py +0 -0
  77. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/assigned_variation.py +0 -0
  78. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/forced_experiment_variation.py +0 -0
  79. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/forced_feature_variation.py +0 -0
  80. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/forced_variation.py +0 -0
  81. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/legal_consent.py +0 -0
  82. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/page_view_visit.py +0 -0
  83. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/visitor.py +0 -0
  84. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/manager/visitor_slot.py +0 -0
  85. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/mapping_identifier.py +0 -0
  86. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/operating_system.py +0 -0
  87. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/page_view.py +0 -0
  88. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/personalization.py +0 -0
  89. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/targeted_segment.py +0 -0
  90. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/unique_identifier.py +0 -0
  91. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/user_agent.py +0 -0
  92. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/data/visitor_visits.py +0 -0
  93. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/defaults.py +0 -0
  94. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/__init__.py +0 -0
  95. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/domain.py +0 -0
  96. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/functions.py +0 -0
  97. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/hasher.py +0 -0
  98. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/logger.py +0 -0
  99. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/nonce.py +0 -0
  100. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/repeat_timer.py +0 -0
  101. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/sem_version.py +0 -0
  102. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/time.py +0 -0
  103. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/helpers/visitor_code.py +0 -0
  104. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/hybrid/__init__.py +0 -0
  105. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/hybrid/hybrid_manager.py +0 -0
  106. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/hybrid/hybrid_manager_impl.py +0 -0
  107. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/kameleoon_client_config.py +0 -0
  108. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/kameleoon_client_factory.py +0 -0
  109. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/logging/__init__.py +0 -0
  110. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/logging/kameleoon_logger.py +0 -0
  111. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/logging/log_level.py +0 -0
  112. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/logging/logger.py +0 -0
  113. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/__init__.py +0 -0
  114. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/asyncexec/__init__.py +0 -0
  115. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/asyncexec/async_executor.py +0 -0
  116. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/data/__init__.py +0 -0
  117. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/data/data_manager.py +0 -0
  118. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/evaluation/__init__.py +0 -0
  119. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/evaluation/evaluated_experiment.py +0 -0
  120. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/remote_data/__init__.py +0 -0
  121. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/remote_data/remote_data_manager.py +0 -0
  122. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/remote_data/remote_visitor_data.py +0 -0
  123. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/tracking/__init__.py +0 -0
  124. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/tracking/tracking_builder.py +0 -0
  125. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/warehouse/__init__.py +0 -0
  126. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/managers/warehouse/warehouse_manager.py +0 -0
  127. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/middleware.py +0 -0
  128. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/__init__.py +0 -0
  129. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/access_token_source_factory.py +0 -0
  130. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/activity_event.py +0 -0
  131. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/cookie/__init__.py +0 -0
  132. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/cookie/cookie_manager.py +0 -0
  133. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/net_provider_impl.py +0 -0
  134. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/query_builder.py +0 -0
  135. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/query_encodable.py +0 -0
  136. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/sendable.py +0 -0
  137. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/service_initialize_context.py +0 -0
  138. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/service_initialize_context_impl.py +0 -0
  139. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/__init__.py +0 -0
  140. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/automation_service.py +0 -0
  141. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/configuration_service.py +0 -0
  142. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/data_service.py +0 -0
  143. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/services/service.py +0 -0
  144. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/uri_helper.py +0 -0
  145. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/network/url_provider.py +0 -0
  146. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/real_time/__init__.py +0 -0
  147. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/real_time/real_time_event.py +0 -0
  148. {kameleoon_client_python-3.20.0/kameleoon/storage → kameleoon_client_python-3.22.1/kameleoon/targeting}/__init__.py +0 -0
  149. {kameleoon_client_python-3.20.0/kameleoon/targeting → kameleoon_client_python-3.22.1/kameleoon/targeting/conditions}/__init__.py +0 -0
  150. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/browser_condition.py +0 -0
  151. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/constants.py +0 -0
  152. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/conversion_condition.py +0 -0
  153. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/cookie_condition.py +0 -0
  154. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/custom_datum.py +0 -0
  155. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/device_condition.py +0 -0
  156. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/exclusive_experiment_condition.py +0 -0
  157. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/geolocation_condition.py +0 -0
  158. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/kcs_heat_range_condition.py +0 -0
  159. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/number_condition.py +0 -0
  160. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/operating_system_condition.py +0 -0
  161. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/page_title_condition.py +0 -0
  162. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/page_url_condition.py +0 -0
  163. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/page_view_number_condition.py +0 -0
  164. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/previous_page_condition.py +0 -0
  165. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/sdk_language_condition.py +0 -0
  166. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/segment_condition.py +0 -0
  167. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/string_value_condition.py +0 -0
  168. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/target_experiment_condition.py +0 -0
  169. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/target_feature_flag_condition.py +0 -0
  170. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/target_personalization_condition.py +0 -0
  171. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/targeting_condition.py +0 -0
  172. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/time_elapsed_since_visit_condition.py +0 -0
  173. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/version_condition.py +0 -0
  174. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/visit_number_today_condition.py +0 -0
  175. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/visit_number_total_condition.py +0 -0
  176. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/visitor_code_condition.py +0 -0
  177. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/visitor_new_return_condition.py +0 -0
  178. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/conditions/visitor_scope_condition.py +0 -0
  179. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/models.py +0 -0
  180. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/targeting_manager.py +0 -0
  181. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/tree_builder.py +0 -0
  182. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/targeting/tree_condition_factory.py +0 -0
  183. {kameleoon_client_python-3.20.0/kameleoon/targeting/conditions → kameleoon_client_python-3.22.1/kameleoon/types}/__init__.py +0 -0
  184. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/data_file.py +0 -0
  185. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/feature_flag.py +0 -0
  186. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/remote_visitor_data_filter.py +0 -0
  187. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/rule.py +0 -0
  188. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/variable.py +0 -0
  189. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon/types/variation.py +0 -0
  190. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon_client_python.egg-info/dependency_links.txt +0 -0
  191. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/kameleoon_client_python.egg-info/top_level.txt +0 -0
  192. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/setup.cfg +0 -0
  193. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/setup.py +0 -0
  194. {kameleoon_client_python-3.20.0/kameleoon/types → kameleoon_client_python-3.22.1/tests/integration/proj}/__init__.py +0 -0
  195. {kameleoon_client_python-3.20.0/tests/integration/proj → kameleoon_client_python-3.22.1/tests/integration/proj/kameleoon_app}/__init__.py +0 -0
  196. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/kameleoon_app/admin.py +0 -0
  197. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/kameleoon_app/apps.py +0 -0
  198. {kameleoon_client_python-3.20.0/tests/integration/proj/kameleoon_app → kameleoon_client_python-3.22.1/tests/integration/proj/kameleoon_app/migrations}/__init__.py +0 -0
  199. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/kameleoon_app/models.py +0 -0
  200. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/kameleoon_app/tests.py +0 -0
  201. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/kameleoon_app/views.py +0 -0
  202. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/manage.py +0 -0
  203. {kameleoon_client_python-3.20.0/tests/integration/proj/kameleoon_app/migrations → kameleoon_client_python-3.22.1/tests/integration/proj/proj}/__init__.py +0 -0
  204. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/proj/asgi.py +0 -0
  205. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/proj/urls.py +0 -0
  206. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/integration/proj/proj/wsgi.py +0 -0
  207. {kameleoon_client_python-3.20.0 → kameleoon_client_python-3.22.1}/tests/resources/config.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kameleoon-client-python
3
- Version: 3.20.0
3
+ Version: 3.22.1
4
4
  Summary: Kameleoon Client Python Software Development Kit.
5
5
  Home-page: https://developers.kameleoon.com/python-sdk.html
6
6
  Author: Kameleoon
@@ -20,27 +20,27 @@ Requires-Dist: requests<3.0.0,>=2.25
20
20
  Requires-Dist: sseclient-py<1.9.0,>=1.7.0
21
21
  Requires-Dist: aiohttp<4.0.0,>=3.8.0
22
22
  Provides-Extra: test
23
+ Requires-Dist: types-python-dateutil==2.8.2; extra == "test"
23
24
  Requires-Dist: twine==3.4.1; extra == "test"
24
- Requires-Dist: pytest-django==4.4.0; extra == "test"
25
+ Requires-Dist: sseclient-py<1.9.0,>=1.7.0; extra == "test"
26
+ Requires-Dist: types-PyYAML==5.4.3; extra == "test"
27
+ Requires-Dist: aiohttp<4.0.0,>=3.8.0; extra == "test"
28
+ Requires-Dist: pyyaml<6.1.0,>=5.4.0; extra == "test"
29
+ Requires-Dist: requests<3.0.0,>=2.25; extra == "test"
30
+ Requires-Dist: pylint==2.12.*; extra == "test"
25
31
  Requires-Dist: pytest-pythonpath==0.7.3; extra == "test"
32
+ Requires-Dist: pytest-django==4.5.2; extra == "test"
26
33
  Requires-Dist: flake8==4.0.1; extra == "test"
34
+ Requires-Dist: pytest-cov==2.12.1; extra == "test"
27
35
  Requires-Dist: wheel==0.36.2; extra == "test"
28
- Requires-Dist: pytest-asyncio==0.21.*; extra == "test"
29
- Requires-Dist: aioresponses==0.7.*; extra == "test"
30
- Requires-Dist: requests<3.0.0,>=2.25; extra == "test"
31
- Requires-Dist: pyyaml<6.1.0,>=5.4.0; extra == "test"
32
- Requires-Dist: mypy==0.991; extra == "test"
33
- Requires-Dist: Werkzeug==2.0.1; extra == "test"
34
36
  Requires-Dist: freezegun==1.1.0; extra == "test"
35
- Requires-Dist: Django==3.2.4; extra == "test"
36
37
  Requires-Dist: types-requests==2.25.0; extra == "test"
37
- Requires-Dist: pytest-cov==2.12.1; extra == "test"
38
- Requires-Dist: pylint==2.12.*; extra == "test"
39
- Requires-Dist: types-PyYAML==5.4.3; extra == "test"
40
38
  Requires-Dist: pytest-ordering==0.6; extra == "test"
41
- Requires-Dist: aiohttp<4.0.0,>=3.8.0; extra == "test"
42
- Requires-Dist: types-python-dateutil==2.8.2; extra == "test"
43
- Requires-Dist: sseclient-py<1.9.0,>=1.7.0; extra == "test"
39
+ Requires-Dist: aioresponses==0.7.*; extra == "test"
40
+ Requires-Dist: mypy==0.991; extra == "test"
41
+ Requires-Dist: Werkzeug==2.0.1; extra == "test"
42
+ Requires-Dist: pytest-asyncio==0.21.*; extra == "test"
43
+ Requires-Dist: Django==4.2.*; extra == "test"
44
44
  Dynamic: author
45
45
  Dynamic: author-email
46
46
  Dynamic: classifier
@@ -0,0 +1,43 @@
1
+ "Async Client Readiness"
2
+
3
+ import asyncio
4
+ from typing import Optional
5
+
6
+ from kameleoon.exceptions import Initialization
7
+ from kameleoon.client_readiness.threading_readiness import ThreadingClientReadiness
8
+
9
+
10
+ class AsyncClientReadiness:
11
+ """
12
+ A class that provides an asynchronous interface for checking client readiness.
13
+
14
+ This class wraps around a `ThreadingClientReadiness` object to provide asynchronous
15
+ methods for waiting for the client to be ready. Blocking waits are run in the
16
+ event loop's default executor so the loop is never blocked.
17
+ """
18
+
19
+ def __init__(self, inner: ThreadingClientReadiness) -> None:
20
+ """
21
+ Initializes an AsyncClientReadiness instance.
22
+ """
23
+ super().__init__()
24
+ self._inner = inner
25
+
26
+ @property
27
+ def inner(self) -> ThreadingClientReadiness:
28
+ """
29
+ Returns the inner ThreadingClientReadiness instance.
30
+ """
31
+ return self._inner
32
+
33
+ async def wait_with_timeout(self, timeout_second: float) -> Optional[Initialization]:
34
+ """
35
+ Asynchronously waits until the readiness state is settled, but no longer than
36
+ `timeout_second`. See `ThreadingClientReadiness.wait_with_timeout` for the
37
+ detailed contract.
38
+ """
39
+ # Non-blocking paths (already settled or expired timeout) are served inline.
40
+ if timeout_second <= 0:
41
+ return self._inner.wait_with_timeout(timeout_second)
42
+ loop = asyncio.get_event_loop()
43
+ return await loop.run_in_executor(None, self._inner.wait_with_timeout, timeout_second)
@@ -0,0 +1,134 @@
1
+ """ThreadingClientReadiness"""
2
+ from threading import Event, Lock
3
+ from typing import Optional
4
+
5
+ from kameleoon.exceptions import Initialization
6
+
7
+
8
+ class _State:
9
+ """
10
+ A single settlement of the readiness state: `error` is written at most once,
11
+ before the event is set, and never changes afterwards, so a waiter which
12
+ obtained the state observes exactly the outcome it was registered for.
13
+ """
14
+
15
+ def __init__(self) -> None:
16
+ self._event = Event()
17
+ self.error: Optional[Initialization] = None
18
+
19
+ def settle(self) -> None:
20
+ """Settles the state, releasing all its waiters."""
21
+ self._event.set()
22
+
23
+ @property
24
+ def settled(self) -> bool:
25
+ """Returns whether the state is settled."""
26
+ return self._event.is_set()
27
+
28
+ def wait(self, timeout_second: float) -> bool:
29
+ """Returns whether the state was settled before the timeout elapsed."""
30
+ return self._event.wait(timeout_second)
31
+
32
+
33
+ class ThreadingClientReadiness:
34
+ """
35
+ ThreadingClientReadiness tracks whether the SDK has successfully loaded its
36
+ configuration and exposes that state to `KameleoonClient.wait_init` and
37
+ `KameleoonClient.is_ready`:
38
+ - a successful fetch settles the state successfully;
39
+ - a failed fetch settles it with an `Initialization` error.
40
+
41
+ Readiness is monotonic: once the SDK is ready it stays ready, and a later failed
42
+ fetch can never revert it. A failure reported before the first success does not
43
+ prevent a subsequent successful retry from marking the SDK ready.
44
+
45
+ It is created on the thread that builds the client but settled from the
46
+ background thread(s) performing configuration fetches.
47
+ """
48
+
49
+ def __init__(self, site_code: str, environment: Optional[str]) -> None:
50
+ self._lock = Lock()
51
+ self._state = _State()
52
+ self._ready = False
53
+ self._site_code = site_code
54
+ self._environment = environment
55
+
56
+ @property
57
+ def ready(self) -> bool:
58
+ """
59
+ Returns `True` if the SDK has been successfully initialized, `False` otherwise
60
+ (including while the initialization is still pending or has failed).
61
+ It never blocks.
62
+
63
+ This is the per-request hot path, so it is lock-free: `_ready` is only ever
64
+ flipped False -> True (readiness is monotonic), making a plain attribute read
65
+ safe. At worst a reader briefly observes `False` while `mark_ready` is
66
+ completing, which is indistinguishable from calling a moment earlier.
67
+ """
68
+ return self._ready
69
+
70
+ def mark_ready(self) -> None:
71
+ """Marks the SDK ready and releases all waiters."""
72
+ with self._lock:
73
+ self._ready = True
74
+ if self._state.settled:
75
+ if self._state.error is not None:
76
+ # Recovery after a failure: the failed state is left settled for its
77
+ # waiters, and a fresh, ready state is published for the new ones.
78
+ state = _State()
79
+ state.settle()
80
+ self._state = state
81
+ else:
82
+ self._state.settle()
83
+
84
+ def mark_not_ready(self, cause: Exception) -> None:
85
+ """
86
+ Reports a failed fetch with an `Initialization` error, but only while the SDK
87
+ is not yet ready. A failure never reverts an already-ready client and is
88
+ reported at most once, so the first reported cause is the one waiters observe.
89
+
90
+ :param cause: the failure which prevented the SDK from loading its configuration.
91
+ :type cause: Exception
92
+ """
93
+ with self._lock:
94
+ if not self._state.settled:
95
+ self._state.error = Initialization(self._site_code, self._environment, cause)
96
+ self._state.settle()
97
+ # else: already ready, or the failure was already reported
98
+
99
+ def wait_with_timeout(self, timeout_second: float) -> Optional[Initialization]:
100
+ """
101
+ Blocks until the readiness state is settled, but no longer than `timeout_second`.
102
+ Returns the result of the configuration fetch: `None` once the SDK is ready, or
103
+ the `Initialization` error the fetch failure was reported with (fail-fast - it
104
+ does not wait for background retries). If no fetch result is available within
105
+ the timeout, returns an `Initialization` error caused by the expired timeout.
106
+ A non-positive timeout expires immediately unless a result is already available.
107
+ An expired timeout does not settle the readiness state: once the SDK becomes
108
+ ready, a subsequent call returns `None`.
109
+
110
+ :param timeout_second: maximum number of seconds to wait.
111
+ :type timeout_second: float
112
+ :return: `None` if the SDK is ready, otherwise an `Initialization` error.
113
+ :rtype: Optional[Initialization]
114
+ """
115
+ state = self._current_state()
116
+ if state.settled:
117
+ return state.error
118
+ if timeout_second <= 0:
119
+ return self._timeout_failure(timeout_second)
120
+ if state.wait(timeout_second):
121
+ return state.error
122
+ return self._timeout_failure(timeout_second)
123
+
124
+ def _timeout_failure(self, timeout_second: float) -> Initialization:
125
+ # The timeout failure wraps a `TimeoutError` cause (the counterpart of the
126
+ # timeout error types the other SDKs report), so a caller can distinguish an
127
+ # expired timeout from a failed fetch.
128
+ cause = TimeoutError(f"initialization did not complete within {round(timeout_second * 1000)} ms")
129
+ return Initialization(self._site_code, self._environment, cause)
130
+
131
+ def _current_state(self) -> _State:
132
+ # Synchronized for safe publication of the state swapped in by a recovery.
133
+ with self._lock:
134
+ return self._state
@@ -67,17 +67,15 @@ class VisitorManager:
67
67
  KameleoonLogger.debug("RETURN: VisitorManager.get_or_create_visitor(visitor_code: %s)", visitor_code)
68
68
 
69
69
  def peek_visitor(self, visitor_code: str) -> Optional[Visitor]:
70
- """Retrieves a visitor by their code if it exists, otherwise returns `None`."""
71
- KameleoonLogger.debug(f"CALL: VisitorManager.peekVisitor(visitor_code: {visitor_code})")
72
- if (slot := self.__try_acquire_slot(visitor_code)) is None:
73
- visitor = None
74
- else:
75
- try:
76
- visitor = slot.visitor
77
- finally:
78
- slot.lock.release()
70
+ """
71
+ Retrieves a visitor by their code if it exists, otherwise returns `None`. Unlike `get_visitor`, does not
72
+ count as visitor activity and takes no lock: a plain read needs no protection against the purge.
73
+ """
74
+ KameleoonLogger.debug("CALL: VisitorManager.peek_visitor(visitor_code: %s)", visitor_code)
75
+ slot = self._slots.get(visitor_code)
76
+ visitor = slot.visitor if slot is not None else None
79
77
  KameleoonLogger.debug(
80
- f"RETURN: VisitorManager.peekVisitor(visitor_code: {visitor_code}) -> " f"(visitor: {visitor})"
78
+ "RETURN: VisitorManager.peek_visitor(visitor_code: %s) -> (visitor: %s)", visitor_code, visitor
81
79
  )
82
80
  return visitor
83
81
 
@@ -180,7 +178,7 @@ class VisitorManager:
180
178
  while True:
181
179
  slot = self._slots[visitor_code]
182
180
  slot.lock.acquire()
183
- if slot == self._slots.get(visitor_code):
181
+ if slot is self._slots.get(visitor_code):
184
182
  return slot
185
183
  slot.lock.release()
186
184
 
@@ -190,7 +188,7 @@ class VisitorManager:
190
188
  if slot is None:
191
189
  return None
192
190
  slot.lock.acquire()
193
- if slot == self._slots.get(visitor_code):
191
+ if slot is self._slots.get(visitor_code):
194
192
  return slot
195
193
  slot.lock.release()
196
194
 
@@ -199,7 +197,7 @@ class VisitorManager:
199
197
  if slot is None:
200
198
  return None
201
199
  slot.lock.acquire()
202
- if slot == self._slots.get(visitor_code):
200
+ if slot is self._slots.get(visitor_code):
203
201
  return slot
204
202
  slot.lock.release()
205
203
  return None
@@ -0,0 +1,19 @@
1
+ """Kameleoon SDK events"""
2
+
3
+ from kameleoon.events.data_file_update_event import DataFileUpdateEvent # noqa: F401
4
+ from kameleoon.events.data_file_update_handler import DataFileUpdateHandler # noqa: F401
5
+ from kameleoon.events.event_handler import EventHandler # noqa: F401
6
+ from kameleoon.events.event_type import EventType # noqa: F401
7
+ from kameleoon.events.http_request_failure import HttpRequestFailure # noqa: F401
8
+ from kameleoon.events.http_request_handler import HttpRequestHandler # noqa: F401
9
+ from kameleoon.events.request_type import RequestType # noqa: F401
10
+
11
+ __all__ = [
12
+ "DataFileUpdateEvent",
13
+ "DataFileUpdateHandler",
14
+ "EventHandler",
15
+ "EventType",
16
+ "HttpRequestFailure",
17
+ "HttpRequestHandler",
18
+ "RequestType",
19
+ ]
@@ -0,0 +1,33 @@
1
+ """Events"""
2
+ from enum import Enum
3
+
4
+
5
+ class DataFileUpdateEvent:
6
+ """Describes an update of the SDK data file (configuration) reported to `DataFileUpdateHandler`."""
7
+
8
+ class Source(Enum):
9
+ """Represents the source of a data file update."""
10
+
11
+ POLLING = "POLLING"
12
+ """The data file was updated by periodic polling."""
13
+
14
+ STREAMING = "STREAMING"
15
+ """The data file was updated by a real-time (streaming) notification."""
16
+
17
+ def __init__(self, source: "DataFileUpdateEvent.Source", date_modified: int) -> None:
18
+ """For internal use only."""
19
+ self.__source = source
20
+ self.__date_modified = date_modified
21
+
22
+ @property
23
+ def source(self) -> "DataFileUpdateEvent.Source":
24
+ """Returns the source of the data file update."""
25
+ return self.__source
26
+
27
+ @property
28
+ def date_modified(self) -> int:
29
+ """Returns the date of the last modification of the data file, in Unix time milliseconds."""
30
+ return self.__date_modified
31
+
32
+ def __str__(self) -> str:
33
+ return f"DataFileUpdateEvent{{Source:{self.__source.value},DateModified:{self.__date_modified}}}"
@@ -0,0 +1,23 @@
1
+ """Events"""
2
+ from typing import Protocol
3
+
4
+ from kameleoon.events.data_file_update_event import DataFileUpdateEvent
5
+ from kameleoon.events.event_handler import EventHandler
6
+
7
+
8
+ class DataFileUpdateHandler(EventHandler, Protocol):
9
+ """
10
+ Handles `EventType.DATAFILE_UPDATE` SDK events.
11
+
12
+ Subclassing this protocol is optional: any object which provides the `on_update` method
13
+ is accepted by `KameleoonClient.set_event_handler`.
14
+ Static type checkers (e.g. mypy) verify such handlers structurally.
15
+ """
16
+
17
+ def on_update(self, update_event: DataFileUpdateEvent) -> None:
18
+ """
19
+ Called when the SDK data file (configuration) is updated.
20
+ :param update_event: The data file update details.
21
+ :type update_event: DataFileUpdateEvent
22
+ """
23
+ raise NotImplementedError()
@@ -0,0 +1,13 @@
1
+ """Events"""
2
+ from typing import Protocol
3
+
4
+
5
+ class EventHandler(Protocol): # pylint: disable=R0903
6
+ """
7
+ Base protocol for all SDK event handlers. See `EventType` for the supported
8
+ event types and their corresponding handler protocols.
9
+
10
+ Subclassing the handler protocols is optional: any object which provides the methods
11
+ required by the selected event type is accepted by `KameleoonClient.set_event_handler`.
12
+ Static type checkers (e.g. mypy) verify such handlers structurally.
13
+ """
@@ -0,0 +1,62 @@
1
+ """Events"""
2
+ from typing import Any, Dict, Optional
3
+
4
+ from kameleoon.events.data_file_update_event import DataFileUpdateEvent
5
+ from kameleoon.events.event_type import EventType, T
6
+ from kameleoon.events.http_request_failure import HttpRequestFailure
7
+ from kameleoon.events.request_type import RequestType
8
+ from kameleoon.logging.kameleoon_logger import KameleoonLogger
9
+
10
+
11
+ class EventManager:
12
+ """For internal use only. Stores SDK event handlers and fires SDK events."""
13
+
14
+ def __init__(self) -> None:
15
+ self.__event_handlers: Dict[EventType[Any], Any] = {}
16
+
17
+ def set_event_handler(self, event_type: EventType[T], handler: Optional[T]) -> None:
18
+ """Sets the SDK event handler for the specified event type. `None` clears the handler."""
19
+ if not isinstance(event_type, EventType): # runtime guard for untyped callers
20
+ KameleoonLogger.error("Unknown event type '%s'", event_type) # type: ignore[unreachable]
21
+ return
22
+ if (handler is not None) and (
23
+ not all(callable(getattr(handler, method, None)) for method in event_type.required_methods)
24
+ ):
25
+ KameleoonLogger.error(
26
+ "Handler for event type '%s' must provide the following methods: %s",
27
+ event_type, ", ".join(event_type.required_methods),
28
+ )
29
+ return
30
+ if handler is None:
31
+ self.__event_handlers.pop(event_type, None)
32
+ else:
33
+ self.__event_handlers[event_type] = handler
34
+
35
+ def fire_http_request_succeeded(self, request_type: RequestType, http_status: int, duration_millis: int) -> None:
36
+ """Fires the HTTP request event for a succeeded HTTP request attempt."""
37
+ handler = self.__event_handlers.get(EventType.HTTP_REQUEST)
38
+ if handler is not None:
39
+ try:
40
+ handler.on_request_succeeded(request_type, http_status, duration_millis)
41
+ except Exception as ex: # pylint: disable=W0703
42
+ KameleoonLogger.warning("HTTP request event handler failed: %s", ex)
43
+
44
+ def fire_http_request_failed(
45
+ self, request_type: RequestType, failure: HttpRequestFailure, duration_millis: int
46
+ ) -> None:
47
+ """Fires the HTTP request event for a failed HTTP request attempt."""
48
+ handler = self.__event_handlers.get(EventType.HTTP_REQUEST)
49
+ if handler is not None:
50
+ try:
51
+ handler.on_request_failed(request_type, failure, duration_millis)
52
+ except Exception as ex: # pylint: disable=W0703
53
+ KameleoonLogger.warning("HTTP request event handler failed: %s", ex)
54
+
55
+ def fire_data_file_update(self, update_event: DataFileUpdateEvent) -> None:
56
+ """Fires the data file update event."""
57
+ handler = self.__event_handlers.get(EventType.DATAFILE_UPDATE)
58
+ if handler is not None:
59
+ try:
60
+ handler.on_update(update_event)
61
+ except Exception as ex: # pylint: disable=W0703
62
+ KameleoonLogger.warning("Data file update event handler failed: %s", ex)
@@ -0,0 +1,70 @@
1
+ """Events"""
2
+ from typing import cast, Generic, Tuple, Type, TypeVar
3
+
4
+ from kameleoon.events.data_file_update_handler import DataFileUpdateHandler
5
+ from kameleoon.events.event_handler import EventHandler
6
+ from kameleoon.events.http_request_handler import HttpRequestHandler
7
+
8
+ T = TypeVar("T", bound=EventHandler)
9
+
10
+
11
+ def _handler_methods(handler_type: type) -> Tuple[str, ...]:
12
+ """Collects the public methods of the handler type."""
13
+ return tuple(
14
+ name for name, member in vars(handler_type).items()
15
+ if not name.startswith("_") and callable(member)
16
+ )
17
+
18
+
19
+ class EventType(Generic[T]):
20
+ """
21
+ Represents an SDK event type available for `KameleoonClient.set_event_handler`.
22
+ The type parameter defines the handler protocol accepted by the event type.
23
+
24
+ Subclassing the handler protocols is optional: any object which provides the methods
25
+ required by the selected event type is accepted by `KameleoonClient.set_event_handler`.
26
+ Static type checkers (e.g. mypy) verify such handlers structurally.
27
+ """
28
+
29
+ HTTP_REQUEST: "EventType[HttpRequestHandler]"
30
+ """
31
+ Notifies when an SDK HTTP request completes successfully or fails.
32
+ Handled by `HttpRequestHandler`.
33
+ """
34
+
35
+ DATAFILE_UPDATE: "EventType[DataFileUpdateHandler]"
36
+ """
37
+ Notifies when the SDK data file (configuration) is updated.
38
+ Handled by `DataFileUpdateHandler`.
39
+ """
40
+
41
+ def __init__(self, name: str, handler_type: type) -> None:
42
+ """For internal use only. Use the predefined event type constants instead."""
43
+ self.__name = name
44
+ self.__handler_type = handler_type
45
+ self.__required_methods = _handler_methods(handler_type)
46
+ if not self.__required_methods:
47
+ # Guards against an event type accepting any object: `all(())` is `True`.
48
+ raise ValueError(f"Event handler type '{handler_type.__name__}' declares no handler methods")
49
+
50
+ @property
51
+ def name(self) -> str:
52
+ """Returns the name of the event type."""
53
+ return self.__name
54
+
55
+ @property
56
+ def handler_type(self) -> Type[T]:
57
+ """Returns the event handler protocol accepted by the event type."""
58
+ return cast(Type[T], self.__handler_type)
59
+
60
+ @property
61
+ def required_methods(self) -> Tuple[str, ...]:
62
+ """Returns the names of the methods a handler of the event type must provide."""
63
+ return self.__required_methods
64
+
65
+ def __str__(self) -> str:
66
+ return self.__name
67
+
68
+
69
+ EventType.HTTP_REQUEST = EventType("HTTP_REQUEST", HttpRequestHandler)
70
+ EventType.DATAFILE_UPDATE = EventType("DATAFILE_UPDATE", DataFileUpdateHandler)
@@ -0,0 +1,70 @@
1
+ """Events"""
2
+ from enum import Enum
3
+ from typing import Optional
4
+
5
+
6
+ class HttpRequestFailure:
7
+ """Describes the failure of an SDK HTTP request reported to `HttpRequestHandler`."""
8
+
9
+ class Reason(Enum):
10
+ """Represents the reason of an SDK HTTP request failure."""
11
+
12
+ HTTP_STATUS = "HTTP_STATUS"
13
+ """The request completed with an unexpected HTTP status code."""
14
+
15
+ EXCEPTION = "EXCEPTION"
16
+ """The request failed with an exception."""
17
+
18
+ CANCELLED = "CANCELLED"
19
+ """The request was cancelled (for example, due to a timeout)."""
20
+
21
+ def __init__(
22
+ self, reason: "HttpRequestFailure.Reason", http_status: Optional[int], cause: Optional[Exception]
23
+ ) -> None:
24
+ """
25
+ For internal use only. Use the `from_http_status`, `from_exception`,
26
+ `of_cancellation` factory methods instead.
27
+ """
28
+ self.__reason = reason
29
+ self.__http_status = http_status
30
+ self.__cause = cause
31
+
32
+ @staticmethod
33
+ def from_http_status(http_status: int) -> "HttpRequestFailure":
34
+ """For internal use only. Creates a failure with the `HTTP_STATUS` reason."""
35
+ return HttpRequestFailure(HttpRequestFailure.Reason.HTTP_STATUS, http_status, None)
36
+
37
+ @staticmethod
38
+ def from_exception(cause: Exception) -> "HttpRequestFailure":
39
+ """For internal use only. Creates a failure with the `EXCEPTION` reason."""
40
+ return HttpRequestFailure(HttpRequestFailure.Reason.EXCEPTION, None, cause)
41
+
42
+ @staticmethod
43
+ def of_cancellation() -> "HttpRequestFailure":
44
+ """For internal use only. Creates a failure with the `CANCELLED` reason."""
45
+ return HttpRequestFailure(HttpRequestFailure.Reason.CANCELLED, None, None)
46
+
47
+ @property
48
+ def reason(self) -> "HttpRequestFailure.Reason":
49
+ """Returns the reason of the failure."""
50
+ return self.__reason
51
+
52
+ @property
53
+ def http_status(self) -> Optional[int]:
54
+ """
55
+ Returns the HTTP status code of the response. Has a value only if `reason` is
56
+ `Reason.HTTP_STATUS`.
57
+ """
58
+ return self.__http_status
59
+
60
+ @property
61
+ def cause(self) -> Optional[Exception]:
62
+ """
63
+ Returns the exception caused the failure. Not `None` only if `reason` is
64
+ `Reason.EXCEPTION`.
65
+ """
66
+ return self.__cause
67
+
68
+ def __str__(self) -> str:
69
+ cause = type(self.__cause).__name__ if self.__cause is not None else None
70
+ return f"HttpRequestFailure{{Reason:{self.__reason.value},HttpStatus:{self.__http_status},Cause:{cause}}}"
@@ -0,0 +1,43 @@
1
+ """Events"""
2
+ from typing import Protocol
3
+
4
+ from kameleoon.events.event_handler import EventHandler
5
+ from kameleoon.events.http_request_failure import HttpRequestFailure
6
+ from kameleoon.events.request_type import RequestType
7
+
8
+
9
+ class HttpRequestHandler(EventHandler, Protocol):
10
+ """
11
+ Handles `EventType.HTTP_REQUEST` SDK events. The handler is called once per each
12
+ actual HTTP request attempt, including retries.
13
+
14
+ Subclassing this protocol is optional: any object which provides the `on_request_succeeded`
15
+ and `on_request_failed` methods is accepted by `KameleoonClient.set_event_handler`.
16
+ Static type checkers (e.g. mypy) verify such handlers structurally.
17
+ """
18
+
19
+ def on_request_succeeded(self, request_type: RequestType, http_status: int, duration_millis: int) -> None:
20
+ """
21
+ Called when an SDK HTTP request completes successfully.
22
+ :param request_type: The type of the request.
23
+ :type request_type: RequestType
24
+ :param http_status: The HTTP status code of the response.
25
+ :type http_status: int
26
+ :param duration_millis: The duration of the request in milliseconds.
27
+ :type duration_millis: int
28
+ """
29
+ raise NotImplementedError()
30
+
31
+ def on_request_failed(
32
+ self, request_type: RequestType, failure: HttpRequestFailure, duration_millis: int
33
+ ) -> None:
34
+ """
35
+ Called when an SDK HTTP request fails.
36
+ :param request_type: The type of the request.
37
+ :type request_type: RequestType
38
+ :param failure: The failure details.
39
+ :type failure: HttpRequestFailure
40
+ :param duration_millis: The duration of the request in milliseconds.
41
+ :type duration_millis: int
42
+ """
43
+ raise NotImplementedError()
@@ -0,0 +1,21 @@
1
+ """Events"""
2
+ from enum import Enum
3
+
4
+
5
+ class RequestType(Enum):
6
+ """Represents the type of an SDK HTTP request reported to `HttpRequestHandler`."""
7
+
8
+ DATAFILE = "DATAFILE"
9
+ """Fetching of the SDK data file (configuration)."""
10
+
11
+ TRACKING = "TRACKING"
12
+ """Sending of tracking data."""
13
+
14
+ REMOTE_VISITOR_DATA = "REMOTE_VISITOR_DATA"
15
+ """Fetching of remote visitor data."""
16
+
17
+ REMOTE_DATA = "REMOTE_DATA"
18
+ """Fetching of remote data."""
19
+
20
+ ACCESS_TOKEN = "ACCESS_TOKEN"
21
+ """Fetching of the Kameleoon API access token."""
@@ -25,6 +25,24 @@ class SiteCodeIsEmpty(KameleoonError):
25
25
  """Site Code is Empty"""
26
26
 
27
27
 
28
+ class Initialization(KameleoonError):
29
+ """
30
+ SDK could not be initialized: its configuration failed to load or no
31
+ initialization result was available within the requested timeout.
32
+ The failure which prevented the initialization is available via `cause`;
33
+ it is also reported in the message so a caller which only logs the
34
+ message still learns why the SDK is not initialized.
35
+ """
36
+
37
+ def __init__(self, site_code: str, environment, cause: Exception) -> None:
38
+ # The failure which prevented the SDK from loading its configuration.
39
+ self.cause = cause
40
+ reason = "unknown" if cause is None else str(cause)
41
+ super().__init__(
42
+ f"SDK is not initialized for siteCode: '{site_code}', environment: '{environment}'. Reason: {reason}"
43
+ )
44
+
45
+
28
46
  class NotFoundError(KameleoonError):
29
47
  """Base not found error"""
30
48