frogml-cli 0.0.1__py3-none-any.whl

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 (287) hide show
  1. frogml_cli/__init__.py +10 -0
  2. frogml_cli/cli.py +40 -0
  3. frogml_cli/commands/__init__.py +0 -0
  4. frogml_cli/commands/_logic/__init__.py +0 -0
  5. frogml_cli/commands/_logic/tools.py +6 -0
  6. frogml_cli/commands/alerts/__init__.py +0 -0
  7. frogml_cli/commands/alerts/alerts_commnad_group.py +18 -0
  8. frogml_cli/commands/alerts/delete/__init__.py +0 -0
  9. frogml_cli/commands/alerts/delete/_logic.py +5 -0
  10. frogml_cli/commands/alerts/delete/ui.py +10 -0
  11. frogml_cli/commands/alerts/list/__init__.py +0 -0
  12. frogml_cli/commands/alerts/list/_logic.py +23 -0
  13. frogml_cli/commands/alerts/list/ui.py +17 -0
  14. frogml_cli/commands/alerts/register/__init__.py +0 -0
  15. frogml_cli/commands/alerts/register/_logic.py +72 -0
  16. frogml_cli/commands/alerts/register/ui.py +30 -0
  17. frogml_cli/commands/audience/__init__.py +0 -0
  18. frogml_cli/commands/audience/_logic/__init__.py +0 -0
  19. frogml_cli/commands/audience/_logic/config/__init__.py +0 -0
  20. frogml_cli/commands/audience/_logic/config/config_base.py +15 -0
  21. frogml_cli/commands/audience/_logic/config/parser.py +30 -0
  22. frogml_cli/commands/audience/_logic/config/v1/__init__.py +0 -0
  23. frogml_cli/commands/audience/_logic/config/v1/audience_config.py +25 -0
  24. frogml_cli/commands/audience/_logic/config/v1/conditions_config.py +59 -0
  25. frogml_cli/commands/audience/_logic/config/v1/config_v1.py +23 -0
  26. frogml_cli/commands/audience/_logic/config/v1/route_config.py +15 -0
  27. frogml_cli/commands/audience/_logic/config/v1/spec.py +9 -0
  28. frogml_cli/commands/audience/audience_api_dump.py +86 -0
  29. frogml_cli/commands/audience/audience_commands_group.py +30 -0
  30. frogml_cli/commands/audience/create/__init__.py +0 -0
  31. frogml_cli/commands/audience/create/logic.py +41 -0
  32. frogml_cli/commands/audience/create/ui.py +21 -0
  33. frogml_cli/commands/audience/delete/__init__.py +0 -0
  34. frogml_cli/commands/audience/delete/logic.py +13 -0
  35. frogml_cli/commands/audience/delete/ui.py +17 -0
  36. frogml_cli/commands/audience/get/__init__.py +0 -0
  37. frogml_cli/commands/audience/get/logic.py +14 -0
  38. frogml_cli/commands/audience/get/ui.py +25 -0
  39. frogml_cli/commands/audience/list/__init__.py +0 -0
  40. frogml_cli/commands/audience/list/logic.py +16 -0
  41. frogml_cli/commands/audience/list/ui.py +26 -0
  42. frogml_cli/commands/audience/update/__init__.py +0 -0
  43. frogml_cli/commands/audience/update/logic.py +37 -0
  44. frogml_cli/commands/audience/update/ui.py +26 -0
  45. frogml_cli/commands/auto_scalling/__init__.py +0 -0
  46. frogml_cli/commands/auto_scalling/_logic/__init__.py +0 -0
  47. frogml_cli/commands/auto_scalling/_logic/config/__init__.py +3 -0
  48. frogml_cli/commands/auto_scalling/_logic/config/config.py +152 -0
  49. frogml_cli/commands/auto_scalling/_logic/config/parser.py +21 -0
  50. frogml_cli/commands/auto_scalling/attach/__init__.py +0 -0
  51. frogml_cli/commands/auto_scalling/attach/_logic.py +43 -0
  52. frogml_cli/commands/auto_scalling/attach/ui.py +21 -0
  53. frogml_cli/commands/auto_scalling/autoscaling_commands_group.py +15 -0
  54. frogml_cli/commands/automations/__init__.py +0 -0
  55. frogml_cli/commands/automations/automations_commands_group.py +30 -0
  56. frogml_cli/commands/automations/delete/__init__.py +0 -0
  57. frogml_cli/commands/automations/delete/_logic.py +6 -0
  58. frogml_cli/commands/automations/delete/ui.py +23 -0
  59. frogml_cli/commands/automations/executions/__init__.py +0 -0
  60. frogml_cli/commands/automations/executions/executions_commands_group.py +14 -0
  61. frogml_cli/commands/automations/executions/list/__init__.py +0 -0
  62. frogml_cli/commands/automations/executions/list/_logic.py +8 -0
  63. frogml_cli/commands/automations/executions/list/ui.py +27 -0
  64. frogml_cli/commands/automations/list/__init__.py +0 -0
  65. frogml_cli/commands/automations/list/_logic.py +36 -0
  66. frogml_cli/commands/automations/list/ui.py +21 -0
  67. frogml_cli/commands/automations/register/__init__.py +0 -0
  68. frogml_cli/commands/automations/register/_logic.py +43 -0
  69. frogml_cli/commands/automations/register/ui.py +44 -0
  70. frogml_cli/commands/config/__init__.py +0 -0
  71. frogml_cli/commands/config/add/__init__.py +0 -0
  72. frogml_cli/commands/config/add/ui.py +62 -0
  73. frogml_cli/commands/config/config_commands_group.py +11 -0
  74. frogml_cli/commands/feature_store/__init__.py +0 -0
  75. frogml_cli/commands/feature_store/backfill/__init__.py +0 -0
  76. frogml_cli/commands/feature_store/backfill/_logic.py +140 -0
  77. frogml_cli/commands/feature_store/backfill/ui.py +129 -0
  78. frogml_cli/commands/feature_store/delete/__init__.py +0 -0
  79. frogml_cli/commands/feature_store/delete/_logic.py +107 -0
  80. frogml_cli/commands/feature_store/delete/ui.py +40 -0
  81. frogml_cli/commands/feature_store/execution/__init__.py +0 -0
  82. frogml_cli/commands/feature_store/execution/ui.py +19 -0
  83. frogml_cli/commands/feature_store/feature_store_command_group.py +29 -0
  84. frogml_cli/commands/feature_store/list/__init__.py +0 -0
  85. frogml_cli/commands/feature_store/list/ui.py +140 -0
  86. frogml_cli/commands/feature_store/pause/__init__.py +0 -0
  87. frogml_cli/commands/feature_store/pause/ui.py +18 -0
  88. frogml_cli/commands/feature_store/register/__init__.py +0 -0
  89. frogml_cli/commands/feature_store/register/_logic.py +369 -0
  90. frogml_cli/commands/feature_store/register/ui.py +111 -0
  91. frogml_cli/commands/feature_store/resume/__init__.py +0 -0
  92. frogml_cli/commands/feature_store/resume/ui.py +18 -0
  93. frogml_cli/commands/feature_store/trigger/__init__.py +0 -0
  94. frogml_cli/commands/feature_store/trigger/ui.py +39 -0
  95. frogml_cli/commands/models/__init__.py +0 -0
  96. frogml_cli/commands/models/build/__init__.py +0 -0
  97. frogml_cli/commands/models/build/_logic/__init__.py +0 -0
  98. frogml_cli/commands/models/build/_logic/build_steps.py +42 -0
  99. frogml_cli/commands/models/build/_logic/client_logs/__init__.py +0 -0
  100. frogml_cli/commands/models/build/_logic/client_logs/cli_phase_run_handler.py +123 -0
  101. frogml_cli/commands/models/build/_logic/client_logs/cli_trigger_build_logger.py +19 -0
  102. frogml_cli/commands/models/build/_logic/client_logs/logger.py +88 -0
  103. frogml_cli/commands/models/build/_logic/client_logs/messages.py +36 -0
  104. frogml_cli/commands/models/build/_logic/client_logs/spinner.py +14 -0
  105. frogml_cli/commands/models/build/_logic/client_logs/trigger_build_logger.py +54 -0
  106. frogml_cli/commands/models/build/_logic/client_logs/utils.py +12 -0
  107. frogml_cli/commands/models/build/_logic/phase/__init__.py +0 -0
  108. frogml_cli/commands/models/build/_logic/phase/a_fetch_model_code/__init__.py +20 -0
  109. frogml_cli/commands/models/build/_logic/phase/a_fetch_model_code/get_sdk_version_step.py +15 -0
  110. frogml_cli/commands/models/build/_logic/phase/b_remote_register_frogml_build/__init__.py +16 -0
  111. frogml_cli/commands/models/build/_logic/phase/c_deploy/__init__.py +6 -0
  112. frogml_cli/commands/models/build/_logic/phase/c_deploy/build_polling_status.py +55 -0
  113. frogml_cli/commands/models/build/_logic/phase/c_deploy/deploy_build.py +61 -0
  114. frogml_cli/commands/models/build/_logic/util/__init__.py +0 -0
  115. frogml_cli/commands/models/build/_logic/util/protobuf_factory.py +45 -0
  116. frogml_cli/commands/models/build/_logic/util/step_decorator.py +60 -0
  117. frogml_cli/commands/models/build/_logic/util/text.py +9 -0
  118. frogml_cli/commands/models/build/_logic/wait_until_finished.py +27 -0
  119. frogml_cli/commands/models/build/ui.py +337 -0
  120. frogml_cli/commands/models/builds/__init__.py +0 -0
  121. frogml_cli/commands/models/builds/builds_commands_group.py +16 -0
  122. frogml_cli/commands/models/builds/cancel/__init__.py +0 -0
  123. frogml_cli/commands/models/builds/cancel/_logic.py +5 -0
  124. frogml_cli/commands/models/builds/cancel/ui.py +15 -0
  125. frogml_cli/commands/models/builds/logs/__init__.py +0 -0
  126. frogml_cli/commands/models/builds/logs/ui.py +35 -0
  127. frogml_cli/commands/models/builds/status/__init__.py +0 -0
  128. frogml_cli/commands/models/builds/status/_logic.py +6 -0
  129. frogml_cli/commands/models/builds/status/ui.py +39 -0
  130. frogml_cli/commands/models/create/__init__.py +0 -0
  131. frogml_cli/commands/models/create/_logic.py +40 -0
  132. frogml_cli/commands/models/create/ui.py +46 -0
  133. frogml_cli/commands/models/delete/__init__.py +0 -0
  134. frogml_cli/commands/models/delete/_logic.py +18 -0
  135. frogml_cli/commands/models/delete/ui.py +25 -0
  136. frogml_cli/commands/models/deployments/__init__.py +0 -0
  137. frogml_cli/commands/models/deployments/deploy/__init__.py +0 -0
  138. frogml_cli/commands/models/deployments/deploy/_logic/__init__.py +0 -0
  139. frogml_cli/commands/models/deployments/deploy/_logic/advance_deployment_options_handler.py +31 -0
  140. frogml_cli/commands/models/deployments/deploy/_logic/base_deploy_executor.py +70 -0
  141. frogml_cli/commands/models/deployments/deploy/_logic/deploy_config.py +261 -0
  142. frogml_cli/commands/models/deployments/deploy/_logic/deployment.py +407 -0
  143. frogml_cli/commands/models/deployments/deploy/_logic/deployment_message_helpers.py +116 -0
  144. frogml_cli/commands/models/deployments/deploy/_logic/deployment_response_handler.py +156 -0
  145. frogml_cli/commands/models/deployments/deploy/_logic/deployment_size_mapper.py +98 -0
  146. frogml_cli/commands/models/deployments/deploy/_logic/get_latest_successful_build.py +28 -0
  147. frogml_cli/commands/models/deployments/deploy/_logic/local_deployment.py +193 -0
  148. frogml_cli/commands/models/deployments/deploy/batch/__init__.py +0 -0
  149. frogml_cli/commands/models/deployments/deploy/batch/_logic/__init__.py +0 -0
  150. frogml_cli/commands/models/deployments/deploy/batch/_logic/advanced_deployment_mapper.py +15 -0
  151. frogml_cli/commands/models/deployments/deploy/batch/_logic/deploy_executor.py +24 -0
  152. frogml_cli/commands/models/deployments/deploy/batch/ui.py +119 -0
  153. frogml_cli/commands/models/deployments/deploy/deploy_commands_group.py +19 -0
  154. frogml_cli/commands/models/deployments/deploy/realtime/__init__.py +0 -0
  155. frogml_cli/commands/models/deployments/deploy/realtime/_logic/__init__.py +0 -0
  156. frogml_cli/commands/models/deployments/deploy/realtime/_logic/advanced_deployment_mapper.py +21 -0
  157. frogml_cli/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py +24 -0
  158. frogml_cli/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py +75 -0
  159. frogml_cli/commands/models/deployments/deploy/realtime/ui.py +202 -0
  160. frogml_cli/commands/models/deployments/deploy/streaming/__init__.py +0 -0
  161. frogml_cli/commands/models/deployments/deploy/streaming/_logic/__init__.py +0 -0
  162. frogml_cli/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py +24 -0
  163. frogml_cli/commands/models/deployments/deploy/streaming/_logic/serving_strategy_mapper.py +38 -0
  164. frogml_cli/commands/models/deployments/deploy/streaming/ui.py +206 -0
  165. frogml_cli/commands/models/deployments/undeploy/__init__.py +0 -0
  166. frogml_cli/commands/models/deployments/undeploy/_logic/__init__.py +0 -0
  167. frogml_cli/commands/models/deployments/undeploy/_logic/request_undeploy.py +249 -0
  168. frogml_cli/commands/models/deployments/undeploy/ui.py +61 -0
  169. frogml_cli/commands/models/describe/__init__.py +0 -0
  170. frogml_cli/commands/models/describe/_logic.py +169 -0
  171. frogml_cli/commands/models/describe/ui.py +35 -0
  172. frogml_cli/commands/models/executions/__init__.py +0 -0
  173. frogml_cli/commands/models/executions/cancel/__init__.py +0 -0
  174. frogml_cli/commands/models/executions/cancel/_logic.py +9 -0
  175. frogml_cli/commands/models/executions/cancel/ui.py +27 -0
  176. frogml_cli/commands/models/executions/execution_commands_group.py +24 -0
  177. frogml_cli/commands/models/executions/report/__init__.py +0 -0
  178. frogml_cli/commands/models/executions/report/_logic.py +14 -0
  179. frogml_cli/commands/models/executions/report/ui.py +43 -0
  180. frogml_cli/commands/models/executions/start/__init__.py +0 -0
  181. frogml_cli/commands/models/executions/start/_logic.py +83 -0
  182. frogml_cli/commands/models/executions/start/ui.py +208 -0
  183. frogml_cli/commands/models/executions/status/__init__.py +0 -0
  184. frogml_cli/commands/models/executions/status/_logic.py +13 -0
  185. frogml_cli/commands/models/executions/status/ui.py +27 -0
  186. frogml_cli/commands/models/init/__init__.py +0 -0
  187. frogml_cli/commands/models/init/_logic/__init__.py +0 -0
  188. frogml_cli/commands/models/init/_logic/initialize_model_structure.py +40 -0
  189. frogml_cli/commands/models/init/_logic/template/__init__.py +0 -0
  190. frogml_cli/commands/models/init/_logic/template/churn/__init__.py +0 -0
  191. frogml_cli/commands/models/init/_logic/template/churn/cookiecutter.json +3 -0
  192. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/__init__.py +0 -0
  193. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  194. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/conda.yml +10 -0
  195. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
  196. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/main/model.py +99 -0
  197. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  198. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  199. frogml_cli/commands/models/init/_logic/template/churn/{{cookiecutter.model_directory}}/tests/it/test_churn.py +32 -0
  200. frogml_cli/commands/models/init/_logic/template/credit_risk/__init__.py +0 -0
  201. frogml_cli/commands/models/init/_logic/template/credit_risk/cookiecutter.json +3 -0
  202. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/__init__.py +0 -0
  203. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  204. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
  205. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/data.csv +1001 -0
  206. frogml_cli/commands/models/init/_logic/template/credit_risk/{{cookiecutter.model_directory}}/main/model.py +108 -0
  207. frogml_cli/commands/models/init/_logic/template/general/__init__.py +0 -0
  208. frogml_cli/commands/models/init/_logic/template/general/cookiecutter.json +6 -0
  209. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/__init__.py +0 -0
  210. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/__init__.py +5 -0
  211. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/conda.yml +8 -0
  212. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.main_directory}}/model.py +66 -0
  213. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/__init__.py +0 -0
  214. frogml_cli/commands/models/init/_logic/template/general/{{cookiecutter.model_directory}}/{{cookiecutter.test_directory}}/test_qwak_model.py +5 -0
  215. frogml_cli/commands/models/init/_logic/template/titanic/__init__.py +0 -0
  216. frogml_cli/commands/models/init/_logic/template/titanic/cookiecutter.json +3 -0
  217. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/__init__.py +0 -0
  218. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  219. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/conda.yml +11 -0
  220. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/main/model.py +98 -0
  221. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  222. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  223. frogml_cli/commands/models/init/_logic/template/titanic/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +24 -0
  224. frogml_cli/commands/models/init/_logic/template/titanic_poetry/__init__.py +0 -0
  225. frogml_cli/commands/models/init/_logic/template/titanic_poetry/cookiecutter.json +3 -0
  226. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/__init__.py +0 -0
  227. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/__init__.py +5 -0
  228. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/model.py +98 -0
  229. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/main/pyproject.toml +20 -0
  230. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/__init__.py +0 -0
  231. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/__init__.py +0 -0
  232. frogml_cli/commands/models/init/_logic/template/titanic_poetry/{{cookiecutter.model_directory}}/tests/it/test_titanic.py +25 -0
  233. frogml_cli/commands/models/init/ui.py +61 -0
  234. frogml_cli/commands/models/list/__init__.py +0 -0
  235. frogml_cli/commands/models/list/_logic.py +5 -0
  236. frogml_cli/commands/models/list/ui.py +41 -0
  237. frogml_cli/commands/models/list_models/__init__.py +0 -0
  238. frogml_cli/commands/models/list_models/_logic.py +11 -0
  239. frogml_cli/commands/models/list_models/ui.py +60 -0
  240. frogml_cli/commands/models/metadata/__init__.py +0 -0
  241. frogml_cli/commands/models/metadata/_logic.py +12 -0
  242. frogml_cli/commands/models/metadata/ui.py +60 -0
  243. frogml_cli/commands/models/models_command_group.py +44 -0
  244. frogml_cli/commands/models/runtime/__init__.py +0 -0
  245. frogml_cli/commands/models/runtime/logs/__init__.py +0 -0
  246. frogml_cli/commands/models/runtime/logs/ui.py +63 -0
  247. frogml_cli/commands/models/runtime/runtime_commands_group.py +17 -0
  248. frogml_cli/commands/models/runtime/update/__init__.py +0 -0
  249. frogml_cli/commands/models/runtime/update/_logic.py +9 -0
  250. frogml_cli/commands/models/runtime/update/ui.py +17 -0
  251. frogml_cli/commands/secrets/__init__.py +0 -0
  252. frogml_cli/commands/secrets/delete/__init__.py +0 -0
  253. frogml_cli/commands/secrets/delete/_logic.py +5 -0
  254. frogml_cli/commands/secrets/delete/ui.py +21 -0
  255. frogml_cli/commands/secrets/get/__init__.py +0 -0
  256. frogml_cli/commands/secrets/get/_logic.py +5 -0
  257. frogml_cli/commands/secrets/get/ui.py +17 -0
  258. frogml_cli/commands/secrets/secrets_commands_group.py +19 -0
  259. frogml_cli/commands/secrets/set/__init__.py +0 -0
  260. frogml_cli/commands/secrets/set/_logic.py +5 -0
  261. frogml_cli/commands/secrets/set/ui.py +16 -0
  262. frogml_cli/commands/ui_tools.py +18 -0
  263. frogml_cli/exceptions/__init__.py +14 -0
  264. frogml_cli/exceptions/frogml_command_exception.py +2 -0
  265. frogml_cli/exceptions/frogml_deploy_new_build_failed.py +5 -0
  266. frogml_cli/exceptions/frogml_resource_not_found.py +2 -0
  267. frogml_cli/inner/__init__.py +0 -0
  268. frogml_cli/inner/file_registry.py +98 -0
  269. frogml_cli/inner/tools/__init__.py +0 -0
  270. frogml_cli/inner/tools/cli_tools.py +179 -0
  271. frogml_cli/inner/tools/config_handler.py +29 -0
  272. frogml_cli/inner/tools/dataclasses_utils.py +21 -0
  273. frogml_cli/inner/tools/logger/__init__.py +3 -0
  274. frogml_cli/inner/tools/logger/logger.py +278 -0
  275. frogml_cli/inner/tools/logger/logging.yml +79 -0
  276. frogml_cli/inner/tools/tracking.py +47 -0
  277. frogml_cli/main.py +9 -0
  278. frogml_cli/tools/__init__.py +0 -0
  279. frogml_cli/tools/colors.py +13 -0
  280. frogml_cli/tools/const.py +3 -0
  281. frogml_cli/tools/files.py +63 -0
  282. frogml_cli/tools/log_handling.py +159 -0
  283. frogml_cli/tools/utils.py +45 -0
  284. frogml_cli-0.0.1.dist-info/METADATA +51 -0
  285. frogml_cli-0.0.1.dist-info/RECORD +287 -0
  286. frogml_cli-0.0.1.dist-info/WHEEL +4 -0
  287. frogml_cli-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,140 @@
1
+ from datetime import datetime
2
+ from typing import List, Tuple
3
+
4
+ import click
5
+ from frogml._proto.qwak.feature_store.features.feature_set_pb2 import (
6
+ FeaturesetSchedulingState,
7
+ FeatureStatus,
8
+ )
9
+ from frogml._proto.qwak.feature_store.features.feature_set_service_pb2 import (
10
+ GetFeaturesetSchedulingStateResponse,
11
+ ListFeatureSetsResponse,
12
+ )
13
+ from frogml.core.clients.feature_store import FeatureRegistryClient
14
+ from tabulate import tabulate
15
+
16
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
17
+
18
+
19
+ def _pts_to_datetime(pts) -> datetime:
20
+ return datetime.fromtimestamp(pts.seconds + pts.nanos / 1e9)
21
+
22
+
23
+ def _datetime_to_str(dt: datetime) -> str:
24
+ return dt.strftime("%Y-%b-%d %H:%M:%S")
25
+
26
+
27
+ def get_featureset_scheduling_state_as_str(
28
+ registry_client: FeatureRegistryClient,
29
+ featureset_name: str,
30
+ ) -> str:
31
+ try:
32
+ resp: GetFeaturesetSchedulingStateResponse = (
33
+ registry_client.get_feature_set_scheduling_state(
34
+ featureset_name=featureset_name
35
+ )
36
+ )
37
+ if resp.state == FeaturesetSchedulingState.SCHEDULING_STATE_PAUSED:
38
+ return "Paused"
39
+ elif resp.state == FeaturesetSchedulingState.SCHEDULING_STATE_ENABLED:
40
+ return "Enabled"
41
+ else:
42
+ return "Unknown"
43
+ except Exception:
44
+ return "Unknown"
45
+
46
+
47
+ def _list_feature_sets_verbose(
48
+ resp: ListFeatureSetsResponse, registry_client: FeatureRegistryClient
49
+ ) -> Tuple[List[str], List[str]]:
50
+ columns = [
51
+ "Name",
52
+ "Type",
53
+ "Status",
54
+ "Scheduling state",
55
+ "Owner",
56
+ "Description",
57
+ "Creation date",
58
+ "Last updated",
59
+ "Featureset id",
60
+ ]
61
+ data = []
62
+ for feature_set in resp.feature_families:
63
+ definition = feature_set.feature_set_definition
64
+ spec = definition.feature_set_spec
65
+ metadata = feature_set.metadata
66
+ if not (spec.feature_set_type.WhichOneof("set_type")):
67
+ feature_set_type = ""
68
+ else:
69
+ feature_set_type = (
70
+ spec.feature_set_type.WhichOneof("set_type")
71
+ .replace("_", " ")
72
+ .capitalize()
73
+ )
74
+
75
+ sched_state: str = "Enabled"
76
+ if spec.feature_set_type.WhichOneof("set_type") in {
77
+ "batch_feature_set",
78
+ "batch_feature_set_v1",
79
+ }:
80
+ sched_state = get_featureset_scheduling_state_as_str(
81
+ registry_client, spec.name
82
+ )
83
+
84
+ data.append(
85
+ [
86
+ spec.name,
87
+ feature_set_type,
88
+ FeatureStatus.Name(definition.status),
89
+ sched_state,
90
+ spec.metadata.owner,
91
+ spec.metadata.description,
92
+ _datetime_to_str(_pts_to_datetime(metadata.created_at)),
93
+ _datetime_to_str(_pts_to_datetime(metadata.last_modified_at)),
94
+ definition.feature_set_id,
95
+ ]
96
+ )
97
+
98
+ return (data, columns)
99
+
100
+
101
+ def _list_feature_sets(
102
+ resp: ListFeatureSetsResponse, registry_client: FeatureRegistryClient
103
+ ) -> Tuple[List[str], List[str]]:
104
+ desired_columns = [
105
+ "Name",
106
+ "Type",
107
+ "Status",
108
+ "Scheduling state",
109
+ "Last updated",
110
+ ]
111
+ verbose_data, verbose_columns = _list_feature_sets_verbose(resp, registry_client)
112
+ col_indexes = [verbose_columns.index(c) for c in desired_columns]
113
+
114
+ def project_lst(lst: List[str]) -> List[str]:
115
+ return [lst[idx] for idx in col_indexes]
116
+
117
+ data = [project_lst(d) for d in verbose_data]
118
+
119
+ return (data, desired_columns)
120
+
121
+
122
+ @click.command("list", cls=FrogMLCommand, help="List registered feature sets")
123
+ @click.option(
124
+ "--verbose",
125
+ "-v",
126
+ default=False,
127
+ is_flag=True,
128
+ metavar="FLAG",
129
+ help="Verbose output",
130
+ )
131
+ def list_feature_sets(verbose: bool, **kwargs):
132
+ registry_client = FeatureRegistryClient()
133
+ response = registry_client.list_feature_sets()
134
+
135
+ if verbose:
136
+ data, columns = _list_feature_sets_verbose(response, registry_client)
137
+ else:
138
+ data, columns = _list_feature_sets(response, registry_client)
139
+
140
+ print(tabulate(data, headers=columns))
File without changes
@@ -0,0 +1,18 @@
1
+ import click
2
+ from frogml.core.clients.feature_store import FeatureRegistryClient
3
+ from frogml.core.exceptions import FrogmlException
4
+
5
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
6
+ from frogml_cli.tools.colors import Color
7
+
8
+
9
+ @click.command("pause", cls=FrogMLCommand, help="Pause a running feature set")
10
+ @click.argument("name")
11
+ def pause_feature_set(name, **kwargs):
12
+ try:
13
+ FeatureRegistryClient().pause_feature_set(feature_set_name=name)
14
+ except Exception as e:
15
+ print(f"{Color.RED} Failed to pause feature set {name} {Color.END}")
16
+ raise FrogmlException(f"Failed to pause feature set {name}") from e
17
+
18
+ print(f"{Color.GREEN}Successfully paused feature set {Color.YELLOW}{name}")
File without changes
@@ -0,0 +1,369 @@
1
+ from pathlib import Path
2
+ from typing import List, Optional, Tuple, cast
3
+
4
+ from frogml._proto.qwak.feature_store.entities.entity_pb2 import (
5
+ EntityDefinition as ProtoEntityDefinition,
6
+ )
7
+ from frogml._proto.qwak.feature_store.features.feature_set_pb2 import (
8
+ Feature as ProtoFeature,
9
+ )
10
+ from frogml._proto.qwak.feature_store.features.feature_set_service_pb2 import (
11
+ GetFeatureSetByNameResponse as ProtoGetFeatureSetByNameResponse,
12
+ )
13
+ from frogml.core.clients.feature_store import FeatureRegistryClient
14
+ from frogml.core.exceptions import FrogmlException
15
+ from frogml.core.feature_store.entities.entity import Entity
16
+ from frogml.core.feature_store.validations.validation_options import (
17
+ FeatureSetValidationOptions,
18
+ )
19
+ from frogml.core.feature_store.validations.validation_response import (
20
+ SuccessValidationResponse,
21
+ ValidationResponse,
22
+ )
23
+ from frogml.feature_store.data_sources.base import BaseSource
24
+ from frogml.feature_store.feature_sets.base_feature_set import BaseFeatureSet
25
+ from tabulate import tabulate
26
+
27
+ from frogml_cli.inner.file_registry import extract_class_objects
28
+ from frogml_cli.inner.tools.cli_tools import ask_yesno
29
+ from frogml_cli.tools.utils import frogml_spinner
30
+
31
+ DELIMITER = "----------------------------------------"
32
+
33
+
34
+ def _register_entities(
35
+ qwak_python_files: List[Tuple[str, str]],
36
+ registry: FeatureRegistryClient,
37
+ force: bool,
38
+ ):
39
+ """
40
+ Register Feature Store Entity Objects
41
+
42
+ Args:
43
+ qwak_python_files: a list of python files containing qwak package imports
44
+ registry: FeatureRegistryClient
45
+ force: boolean determining if to force register all encountered Entity objects
46
+ """
47
+ with frogml_spinner(
48
+ begin_text="Finding Entities to register", print_callback=print
49
+ ):
50
+ qwak_entities: List[Tuple[Entity, str]] = extract_class_objects(
51
+ qwak_python_files, Entity
52
+ )
53
+
54
+ print(f"👀 Found {len(qwak_entities)} Entities")
55
+ for entity, source_file_path in qwak_entities:
56
+ existing_entity = registry.get_entity_by_name(entity.name)
57
+ if existing_entity:
58
+ if ask_yesno(
59
+ f"Update existing Entity '{entity.name}' from source file '{source_file_path}'?",
60
+ force,
61
+ ):
62
+ registry.update_entity(
63
+ existing_entity.entity.entity_definition.entity_id,
64
+ entity._to_proto(),
65
+ )
66
+ else:
67
+ if ask_yesno(
68
+ f"Create new Entity '{entity.name}' from source file '{source_file_path}'?",
69
+ force,
70
+ ):
71
+ registry.create_entity(entity._to_proto())
72
+ print(DELIMITER)
73
+
74
+
75
+ def _register_data_sources(
76
+ qwak_python_files: List[Tuple[str, str]],
77
+ registry: FeatureRegistryClient,
78
+ force: bool,
79
+ no_validation: bool,
80
+ ignore_validation_errors: bool,
81
+ ):
82
+ """
83
+ Register Feature Store Data Source Objects
84
+
85
+ Args:
86
+ qwak_python_files: a list of python files containing qwak package imports
87
+ registry: FeatureRegistryClient
88
+ force: boolean determining if to force register all encountered Data Source objects
89
+ no_validation: whether to validate entities
90
+ ignore_validation_errors: whether to ignore and continue registering objects after encountering validation errors
91
+ """
92
+ with frogml_spinner(
93
+ begin_text="Finding Data Sources to register", print_callback=print
94
+ ):
95
+ qwak_sources: List[Tuple[BaseSource, str]] = extract_class_objects(
96
+ qwak_python_files, BaseSource
97
+ )
98
+
99
+ print(f"👀 Found {len(qwak_sources)} Data Sources")
100
+ for data_source, source_file_path in qwak_sources:
101
+ validation_failed = False
102
+ artifact_url: Optional[str] = None
103
+
104
+ if no_validation:
105
+ print("Skipping data source validation")
106
+ else:
107
+ try:
108
+ artifact_url = _handle_data_source_validation(data_source)
109
+ except Exception as e:
110
+ print(str(e))
111
+ validation_failed = True
112
+
113
+ if validation_failed and not ignore_validation_errors:
114
+ print("Not continuing to registration due to failure in validation")
115
+ exit(1)
116
+
117
+ existing_source = registry.get_data_source_by_name(data_source.name)
118
+ if existing_source:
119
+ if ask_yesno(
120
+ f"Update existing Data Source '{data_source.name}' from source file '{source_file_path}'?",
121
+ force,
122
+ ):
123
+ data_source_proto, _ = data_source._prepare_and_get(
124
+ artifact_url=artifact_url,
125
+ source_definition_path=Path(source_file_path),
126
+ )
127
+ registry.update_data_source(
128
+ existing_source.data_source.data_source_definition.data_source_id,
129
+ data_source_proto,
130
+ )
131
+ else:
132
+ if ask_yesno(
133
+ f"Create Data Source '{data_source.name}' from source file '{source_file_path}'?",
134
+ force,
135
+ ):
136
+ data_source_proto, _ = data_source._prepare_and_get(
137
+ artifact_url=artifact_url,
138
+ source_definition_path=Path(source_file_path),
139
+ )
140
+ registry.create_data_source(data_source_proto)
141
+ print(DELIMITER)
142
+
143
+
144
+ def _handle_data_source_validation(
145
+ data_source: BaseSource,
146
+ ) -> Optional[str]:
147
+ print(f"Validating '{data_source.name}' data source")
148
+ with frogml_spinner(begin_text="", print_callback=print):
149
+ from frogml.feature_store.validations.validator import FeaturesOperatorValidator
150
+
151
+ v = FeaturesOperatorValidator()
152
+ response: ValidationResponse
153
+ artifact_url: Optional[str]
154
+
155
+ response, artifact_url = v.validate_data_source(data_source=data_source)
156
+ if isinstance(response, SuccessValidationResponse):
157
+ print_validation_outputs(response.stdout, response.stderr)
158
+ print("✅ Validation completed successfully, got data source columns:")
159
+
160
+ table = [(x.feature_name, x.feature_type) for x in response.features]
161
+ print(tabulate(table, headers=["column name", "type"]))
162
+
163
+ return artifact_url
164
+ else:
165
+ raise FrogmlException(f"🧨 Validation failed: \n{response}")
166
+
167
+
168
+ def _register_features_sets(
169
+ qwak_python_files: List[Tuple[str, str]],
170
+ registry: FeatureRegistryClient,
171
+ force: bool,
172
+ git_commit: str,
173
+ no_validation: bool,
174
+ ignore_validation_errors: bool,
175
+ data_source_limit: Optional[int] = None,
176
+ ):
177
+ """
178
+ Register Feature Store Feature Set Objects
179
+
180
+ Args:
181
+ qwak_python_files: a list of python files containing qwak package imports
182
+ registry: FeatureRegistryClient
183
+ force: boolean determining if to force register all encountered Feature Set objects
184
+ git_commit: the git commit of the parent folder
185
+ no_validation: whether to validate entities
186
+ ignore_validation_errors: whether to ignore and continue registering objects after encountering validation errors
187
+ """
188
+ with frogml_spinner(
189
+ begin_text="Finding Feature Sets to register", print_callback=print
190
+ ):
191
+ qwak_feature_sets = extract_class_objects(qwak_python_files, BaseFeatureSet)
192
+
193
+ print(f"👀 Found {len(qwak_feature_sets)} Feature Set(s)")
194
+
195
+ for featureset, source_file_path in qwak_feature_sets:
196
+ featureset = cast(BaseFeatureSet, featureset)
197
+ existing_feature_set: ProtoGetFeatureSetByNameResponse = (
198
+ registry.get_feature_set_by_name(featureset.name)
199
+ )
200
+
201
+ registration: bool = False
202
+ if existing_feature_set:
203
+ # Provide entity information of registered feature set before any other operation
204
+ if featureset.key:
205
+ featureset.entity = (
206
+ existing_feature_set.feature_set.feature_set_definition.feature_set_spec.entity.entity_spec.name
207
+ )
208
+
209
+ registration = ask_yesno(
210
+ f"Update existing feature set '{featureset.name}' from source file '{source_file_path}'?", # nosec B608
211
+ force,
212
+ )
213
+ else:
214
+ registration = ask_yesno(
215
+ f"Create new feature set '{featureset.name}' from source file '{source_file_path}'?",
216
+ force,
217
+ )
218
+
219
+ if registration:
220
+ features: Optional[List[ProtoFeature]] = []
221
+ artifact_url: Optional[str] = None
222
+
223
+ features, artifact_url = _validate_featureset(
224
+ featureset=featureset,
225
+ no_validation=no_validation,
226
+ ignore_validation_errors=ignore_validation_errors,
227
+ data_source_limit=data_source_limit,
228
+ )
229
+
230
+ proto_feature_set, _ = featureset._to_proto(
231
+ git_commit=git_commit,
232
+ features=features,
233
+ feature_registry=registry,
234
+ artifact_url=artifact_url,
235
+ )
236
+
237
+ if existing_feature_set:
238
+ registry.update_feature_set(
239
+ existing_feature_set.feature_set.feature_set_definition.feature_set_id,
240
+ proto_feature_set,
241
+ )
242
+ else:
243
+ _register_new_feature_set(
244
+ new_feature_set=featureset,
245
+ entity_key=proto_feature_set.entity,
246
+ registry=registry,
247
+ git_commit=git_commit,
248
+ features=features,
249
+ artifact_url=artifact_url,
250
+ )
251
+
252
+
253
+ def _register_new_feature_set(
254
+ new_feature_set: BaseFeatureSet,
255
+ entity_key: ProtoEntityDefinition,
256
+ registry: FeatureRegistryClient,
257
+ git_commit: str,
258
+ features: Optional[List[ProtoFeature]],
259
+ artifact_url: Optional[str] = None,
260
+ ):
261
+ # Create entity for the defined key and set it as the fs's entity
262
+ if new_feature_set.key:
263
+ new_entity: Entity = Entity._from_proto(proto=entity_key)
264
+ try:
265
+ new_entity.register()
266
+ except FrogmlException:
267
+ raise FrogmlException(
268
+ f"Failed to create key for {new_feature_set.name}, "
269
+ f"aborting feature set creation"
270
+ )
271
+ new_feature_set.entity = new_entity.name
272
+
273
+ # this is done to retrieve the entities metadata
274
+ proto_feature_set, _ = new_feature_set._to_proto(
275
+ git_commit, features, registry, artifact_url=artifact_url
276
+ )
277
+
278
+ try:
279
+ registry.create_feature_set(proto_feature_set)
280
+ # rollback entity creation in case fs registration failed
281
+ except FrogmlException as e1:
282
+ try:
283
+ if new_feature_set.key:
284
+ registry.delete_entity(entity_id=proto_feature_set.entity.entity_id)
285
+ except FrogmlException:
286
+ raise e1
287
+ raise e1
288
+
289
+
290
+ def _handle_featureset_validation(
291
+ featureset: BaseFeatureSet,
292
+ data_source_limit: Optional[int] = None,
293
+ ) -> Tuple[List[ProtoFeature], Optional[str]]:
294
+ print(f"Validating '{featureset.name}' feature set")
295
+ with frogml_spinner(begin_text="", print_callback=print):
296
+ from frogml.feature_store.validations.validator import FeaturesOperatorValidator
297
+
298
+ v = FeaturesOperatorValidator()
299
+ response: ValidationResponse
300
+ artifact_url: Optional[str]
301
+ validation_options = FeatureSetValidationOptions(
302
+ data_source_limit=data_source_limit
303
+ )
304
+
305
+ response, artifact_url = v.validate_featureset(
306
+ featureset=featureset, validation_options=validation_options
307
+ )
308
+ if isinstance(response, SuccessValidationResponse):
309
+ print_validation_outputs(response.stdout, response.stderr)
310
+ print("✅ Validation completed successfully, got data source columns:")
311
+ table = [(x.feature_name, x.feature_type) for x in response.features]
312
+ print(tabulate(table, headers=["column name", "type"]))
313
+ return response.features, artifact_url
314
+ else:
315
+ raise FrogmlException(f"🧨 Validation failed: \n{response}")
316
+
317
+
318
+ def _validate_featureset(
319
+ featureset: BaseFeatureSet,
320
+ no_validation: bool,
321
+ ignore_validation_errors: bool,
322
+ data_source_limit: Optional[int] = None,
323
+ ) -> Tuple[List[ProtoFeature], Optional[str]]:
324
+ """
325
+ Validates featureset transformation
326
+ Args:
327
+ featureset: BaseFeatureSet featureset
328
+ no_validation: skip validation
329
+ operator: Operator client
330
+ registry: Registry client
331
+ Returns:
332
+ Optional list of features returned from validation
333
+ """
334
+ features: List[ProtoFeature] = []
335
+ artifact_url: Optional[str] = None
336
+ if not no_validation:
337
+ try:
338
+ features, artifact_url = _handle_featureset_validation(
339
+ featureset=featureset, data_source_limit=data_source_limit
340
+ )
341
+ except Exception as e:
342
+ print(str(e))
343
+
344
+ if not ignore_validation_errors:
345
+ print("Not continuing to registration due to failure in validation")
346
+ exit(1)
347
+ else:
348
+ print("Ignoring validation errors")
349
+ else:
350
+ print(f"Skipping validation for '{featureset.name}' feature set")
351
+ return features, artifact_url
352
+
353
+
354
+ def print_validation_outputs(stdout: str, stderr: str) -> bool:
355
+ did_print = False
356
+
357
+ if stdout or stderr:
358
+ message = "Validation outputs: "
359
+
360
+ if stdout:
361
+ message += f"stdout: {stdout}\n "
362
+
363
+ if stderr:
364
+ message += f"stderr: {stderr}"
365
+
366
+ print(message)
367
+ did_print = True
368
+
369
+ return did_print
@@ -0,0 +1,111 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import List, Optional, Tuple
4
+
5
+ import click
6
+ from frogml.core.clients.feature_store import FeatureRegistryClient
7
+
8
+ from frogml_cli.commands.feature_store.register._logic import (
9
+ _register_data_sources,
10
+ _register_entities,
11
+ _register_features_sets,
12
+ )
13
+ from frogml_cli.inner.file_registry import list_qwak_python_files
14
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
15
+ from frogml_cli.tools.utils import frogml_spinner
16
+
17
+
18
+ @click.command(
19
+ "register",
20
+ cls=FrogMLCommand,
21
+ help="Register and deploy all feature store object under the given path. Registered "
22
+ "features will be visible on the Frogml management platform after registration",
23
+ )
24
+ @click.option(
25
+ "--path",
26
+ "-p",
27
+ type=click.Path(exists=True),
28
+ metavar="PATH",
29
+ help="Directory / module where frogml feature store objects are stored",
30
+ )
31
+ @click.option(
32
+ "--force",
33
+ "-f",
34
+ default=False,
35
+ is_flag=True,
36
+ metavar="FLAG",
37
+ help="Force register all found frogml Feature Store objects",
38
+ )
39
+ @click.option(
40
+ "--no-validation",
41
+ "-nv",
42
+ default=False,
43
+ is_flag=True,
44
+ metavar="FLAG",
45
+ help="Skip validation for all found frogml Feature Store objects",
46
+ )
47
+ @click.option(
48
+ "--ignore-validation-errors",
49
+ "-ive",
50
+ default=False,
51
+ is_flag=True,
52
+ metavar="FLAG",
53
+ help="Ignore validation errors. Continue registering even if an error occurs",
54
+ )
55
+ @click.option(
56
+ "--fs-validation-ds-limit",
57
+ type=click.IntRange(1, 10000),
58
+ help="Limit the number of records to be fetched from each datasource during featureset validation",
59
+ )
60
+ def register_fs_objects(
61
+ path: Path,
62
+ force: bool,
63
+ no_validation: bool,
64
+ ignore_validation_errors: bool,
65
+ fs_validation_ds_limit: Optional[int],
66
+ **kwargs,
67
+ ):
68
+ qwak_python_files: List[Tuple[str, str]]
69
+
70
+ if not path:
71
+ path = Path.cwd()
72
+ else:
73
+ path = Path(path)
74
+
75
+ if path.is_file():
76
+ qwak_python_files = [(str(path), os.path.abspath(path))]
77
+ elif Path.is_dir(path):
78
+ with frogml_spinner(
79
+ begin_text="Recursively looking for python files in input dir",
80
+ print_callback=print,
81
+ ) as sp:
82
+ qwak_python_files = list_qwak_python_files(path, sp)
83
+
84
+ try:
85
+ import git
86
+
87
+ git_commit = git.Repo(path, search_parent_directories=True).head.commit.hexsha
88
+ except Exception:
89
+ # be super defensive on Git errors. Failing to fetch anything git related should not fail the registration
90
+ git_commit = None
91
+
92
+ registry_client = FeatureRegistryClient()
93
+ _register_entities(qwak_python_files, registry_client, force)
94
+
95
+ _register_data_sources(
96
+ qwak_python_files,
97
+ registry_client,
98
+ force,
99
+ no_validation,
100
+ ignore_validation_errors,
101
+ )
102
+
103
+ _register_features_sets(
104
+ qwak_python_files,
105
+ registry_client,
106
+ force,
107
+ git_commit,
108
+ no_validation,
109
+ ignore_validation_errors,
110
+ fs_validation_ds_limit,
111
+ )
File without changes
@@ -0,0 +1,18 @@
1
+ import click
2
+ from frogml.core.clients.feature_store import FeatureRegistryClient
3
+ from frogml.core.exceptions import FrogmlException
4
+
5
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
6
+ from frogml_cli.tools.colors import Color
7
+
8
+
9
+ @click.command("resume", cls=FrogMLCommand, help="Resume a paused feature set")
10
+ @click.argument("name")
11
+ def resume_feature_set(name, **kwargs):
12
+ try:
13
+ FeatureRegistryClient().resume_feature_set(feature_set_name=name)
14
+ except Exception as e:
15
+ print(f"{Color.RED} Failed to resume feature set {name} {Color.END}")
16
+ raise FrogmlException(f"Failed to resume feature set {name}") from e
17
+
18
+ print(f"{Color.GREEN}Successfully resume feature set {Color.YELLOW}{name}")
File without changes