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,249 @@
1
+ from time import sleep
2
+ from typing import Dict, List, Set
3
+
4
+ from frogml._proto.qwak.audience.v1.audience_pb2 import AudienceRoutesEntry
5
+ from frogml._proto.qwak.deployment.deployment_pb2 import (
6
+ DeploymentHostingServiceType,
7
+ EnvironmentDeploymentDetailsMessage,
8
+ EnvironmentUndeploymentMessage,
9
+ ModelDeploymentStatus,
10
+ TrafficConfig,
11
+ )
12
+ from frogml._proto.qwak.ecosystem.v0.ecosystem_pb2 import EnvironmentDetails
13
+ from frogml.core.clients.administration.eco_system.client import EcosystemClient
14
+ from frogml.core.clients.deployment.client import DeploymentManagementClient
15
+ from frogml.core.exceptions import FrogmlException
16
+ from frogml.core.tools.logger.logger import get_frogml_logger
17
+
18
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
19
+ DeployConfig,
20
+ )
21
+ from frogml_cli.tools.utils import frogml_spinner
22
+
23
+ NO_DEPLOYED_VARIATIONS_ERROR_MSG = (
24
+ "There are currently no deployed variations for model {model_id} in {env_name}"
25
+ )
26
+ UNDEPLOY_ERROR_MSG = "Environment {environment_id} failed with status: {status}"
27
+ logger = get_frogml_logger()
28
+
29
+ FAILED_UNDEPLOYMENT_STATUS = ["FAILED_UNDEPLOYMENT"]
30
+ SUCCESSFUL_UNDEPLOYMENT_STATUS = ["SUCCESSFUL_UNDEPLOYMENT"]
31
+ END_UNDEPLOYMENT_STATUSES = SUCCESSFUL_UNDEPLOYMENT_STATUS + FAILED_UNDEPLOYMENT_STATUS
32
+ TIME_TO_WAIT_FOR_UNDEPLOYMENT_POLLING = 5
33
+
34
+
35
+ def get_deployed_variation_name(existing_variations_names: Set[str]) -> str:
36
+ return list(existing_variations_names)[0]
37
+
38
+
39
+ def get_environment_undeploy_message(
40
+ audiences: List[AudienceRoutesEntry],
41
+ existing_variations_names: Set[str],
42
+ fallback_variation: str,
43
+ model_id: str,
44
+ model_uuid: str,
45
+ variation_name: str,
46
+ ):
47
+ if not variation_name and len(existing_variations_names) == 1:
48
+ variation_name = get_deployed_variation_name(
49
+ existing_variations_names=existing_variations_names
50
+ )
51
+
52
+ return EnvironmentUndeploymentMessage(
53
+ model_id=model_id,
54
+ model_uuid=model_uuid,
55
+ hosting_service_type=DeploymentHostingServiceType.KUBE_DEPLOYMENT,
56
+ traffic_config=TrafficConfig(
57
+ selected_variation_name=variation_name,
58
+ audience_routes_entries=audiences,
59
+ fallback_variation=fallback_variation,
60
+ ),
61
+ )
62
+
63
+
64
+ def get_env_to_undeploy_message(
65
+ audiences: List[AudienceRoutesEntry],
66
+ model_uuid: str,
67
+ env_id_to_deployment_details: Dict[str, EnvironmentDeploymentDetailsMessage],
68
+ env_name_to_env_details: Dict[str, EnvironmentDetails],
69
+ model_id: str,
70
+ variation_name: str,
71
+ fallback_variation: str,
72
+ ) -> Dict[str, EnvironmentUndeploymentMessage]:
73
+ env_undeployment_requests = dict()
74
+ errors = []
75
+ for env_name, env_details in env_name_to_env_details.items():
76
+ env_deployments_details_message = env_id_to_deployment_details.get(
77
+ env_details.id
78
+ )
79
+ if not env_deployments_details_message:
80
+ errors.append(
81
+ NO_DEPLOYED_VARIATIONS_ERROR_MSG.format(
82
+ model_id=model_id, env_name=env_name
83
+ )
84
+ )
85
+ continue
86
+
87
+ env_deployments_details = env_deployments_details_message.deployments_details
88
+ existing_variations_names = {
89
+ deployment.variation.name for deployment in env_deployments_details
90
+ }
91
+ try:
92
+ env_undeployment_requests[
93
+ env_details.id
94
+ ] = get_environment_undeploy_message(
95
+ audiences,
96
+ existing_variations_names,
97
+ fallback_variation,
98
+ model_id,
99
+ model_uuid,
100
+ variation_name,
101
+ )
102
+ except FrogmlException as e:
103
+ errors.append(e.message)
104
+ if errors:
105
+ raise FrogmlException("\n".join(errors))
106
+ return env_undeployment_requests
107
+
108
+
109
+ def undeploy(
110
+ model_id: str,
111
+ config: DeployConfig,
112
+ model_uuid: str = "",
113
+ sync: bool = False,
114
+ ):
115
+ deployment_client = DeploymentManagementClient()
116
+ ecosystem_client = EcosystemClient()
117
+ audiences: List[AudienceRoutesEntry] = [
118
+ audience.to_audience_route_entry(index)
119
+ for index, audience in enumerate(config.realtime.audiences)
120
+ ]
121
+
122
+ if not model_uuid:
123
+ raise FrogmlException("missing argument model uuid")
124
+
125
+ environments_names = config.realtime.environments if config.realtime else []
126
+
127
+ deployment_details = deployment_client.get_deployment_details(model_id, model_uuid)
128
+ env_id_to_deployment_details = dict(
129
+ deployment_details.environment_to_deployment_details
130
+ )
131
+ env_name_to_env_details = ecosystem_client.get_environments_names_to_details(
132
+ environments_names
133
+ )
134
+
135
+ env_undeployment_requests = get_env_to_undeploy_message(
136
+ audiences,
137
+ model_uuid,
138
+ env_id_to_deployment_details,
139
+ env_name_to_env_details,
140
+ model_id,
141
+ config.realtime.variation_name,
142
+ config.realtime.fallback_variation,
143
+ )
144
+
145
+ environment_to_deployment_id = {}
146
+ if sync:
147
+ environment_to_deployment_id = __get_environment_to_deployment_id(
148
+ deployment_client, model_id, model_uuid, env_undeployment_requests
149
+ )
150
+
151
+ undeployment_response = deployment_client.undeploy_model(
152
+ model_id=model_id,
153
+ model_uuid=model_uuid,
154
+ env_undeployment_requests=env_undeployment_requests,
155
+ )
156
+
157
+ if sync:
158
+ __sync_undeploy(
159
+ environment_to_deployment_id=environment_to_deployment_id,
160
+ model_id=model_id,
161
+ deployment_client=deployment_client,
162
+ )
163
+ logger.info(
164
+ f"Current status is {ModelDeploymentStatus.Name(undeployment_response.status)}."
165
+ )
166
+
167
+ return undeployment_response
168
+
169
+
170
+ def __get_environment_to_deployment_id(
171
+ deployment_client: DeploymentManagementClient,
172
+ model_id: str,
173
+ model_uuid: str,
174
+ env_undeployment_requests: Dict,
175
+ ) -> Dict:
176
+ deployment_details = deployment_client.get_deployment_details(
177
+ model_id, model_uuid
178
+ ).environment_to_deployment_details
179
+ result = {}
180
+ for env_id, env_deployment_details in env_undeployment_requests.items():
181
+ deployment_detail_by_env = deployment_details.get(env_id)
182
+ if deployment_detail_by_env:
183
+ for deployment_detail in deployment_detail_by_env.deployments_details:
184
+ if (
185
+ env_id == deployment_detail.environment_id
186
+ and deployment_detail.variation.name
187
+ == env_deployment_details.traffic_config.selected_variation_name
188
+ ):
189
+ result[env_id] = deployment_detail.deployment_id
190
+ return result
191
+
192
+
193
+ def __sync_undeploy(
194
+ environment_to_deployment_id: Dict,
195
+ model_id: str,
196
+ deployment_client: DeploymentManagementClient,
197
+ ):
198
+ with frogml_spinner(
199
+ begin_text=f"Undeploy - model: {model_id}",
200
+ end_text="Successful undeployment",
201
+ print_callback=print,
202
+ ):
203
+ for environment_id, deployment_id in environment_to_deployment_id.items():
204
+ status_result = _poll_undeployment_status(
205
+ deployment_id=deployment_id,
206
+ deployment_client=deployment_client,
207
+ check_every_n_seconds=TIME_TO_WAIT_FOR_UNDEPLOYMENT_POLLING,
208
+ )
209
+ failed_to_undeployment = []
210
+ for _, status in status_result.items():
211
+ if status in FAILED_UNDEPLOYMENT_STATUS:
212
+ failed_to_undeployment.append(
213
+ UNDEPLOY_ERROR_MSG.format(
214
+ environment_id=environment_id, status=status
215
+ )
216
+ )
217
+ if failed_to_undeployment:
218
+ raise FrogmlException("\n".join(failed_to_undeployment))
219
+
220
+
221
+ def _poll_undeployment_status(
222
+ deployment_id: str,
223
+ deployment_client: DeploymentManagementClient,
224
+ check_every_n_seconds: int,
225
+ ) -> Dict:
226
+ deployment_status = ""
227
+
228
+ while deployment_status not in END_UNDEPLOYMENT_STATUSES:
229
+ sleep(check_every_n_seconds)
230
+ deployment_status = __get_deployment_status(
231
+ deployment_id=deployment_id, deployment_client=deployment_client
232
+ )
233
+ return {deployment_id: deployment_status}
234
+
235
+
236
+ def __get_deployment_status(
237
+ deployment_id: str, deployment_client: DeploymentManagementClient
238
+ ):
239
+ try:
240
+ return ModelDeploymentStatus.Name(
241
+ deployment_client.get_deployment_status(
242
+ deployment_named_id=deployment_id,
243
+ ).status
244
+ )
245
+ except FrogmlException as e:
246
+ logger.error(
247
+ f"Got error while trying to get deployment id: {deployment_id} status. Error is: {e.message}"
248
+ )
249
+ return ""
@@ -0,0 +1,61 @@
1
+ import click
2
+ from frogml.core.clients.model_management import ModelsManagementClient
3
+ from frogml.core.tools.logger.logger import get_frogml_logger
4
+
5
+ from frogml_cli.commands.models.deployments.deploy._logic.deploy_config import (
6
+ DeployConfig,
7
+ )
8
+ from frogml_cli.commands.models.deployments.undeploy._logic.request_undeploy import (
9
+ undeploy,
10
+ )
11
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
12
+ from frogml_cli.inner.tools.config_handler import config_handler
13
+
14
+ logger = get_frogml_logger()
15
+
16
+
17
+ @click.command(
18
+ name="undeploy",
19
+ help="Model undeploy operation",
20
+ cls=FrogMLCommand,
21
+ )
22
+ @click.option("--model-id", metavar="NAME", required=True, help="Model ID")
23
+ @click.option(
24
+ "--variation-name",
25
+ required=False,
26
+ type=str,
27
+ help="The variation name",
28
+ )
29
+ @click.option(
30
+ "-f",
31
+ "--from-file",
32
+ help="Undeploy by run_config file, Command arguments will overwrite any run_config.",
33
+ required=False,
34
+ type=click.Path(exists=True, resolve_path=True, dir_okay=False),
35
+ )
36
+ @click.option(
37
+ "--sync",
38
+ is_flag=True,
39
+ default=False,
40
+ help="Waiting for deployments to be undeploy",
41
+ )
42
+ def models_undeploy(
43
+ model_id: str,
44
+ variation_name: str,
45
+ from_file: str = None,
46
+ sync: bool = False,
47
+ **kwargs,
48
+ ):
49
+ logger.info(f"Initiating undeployment for model '{model_id}'")
50
+ models_management = ModelsManagementClient()
51
+ config: DeployConfig = config_handler(
52
+ config=DeployConfig,
53
+ from_file=from_file,
54
+ out_conf=False,
55
+ sections=("realtime",),
56
+ model_id=model_id,
57
+ variation_name=variation_name,
58
+ )
59
+ model_uuid = models_management.get_model_uuid(model_id)
60
+
61
+ undeploy(model_id=model_id, config=config, model_uuid=model_uuid, sync=sync)
File without changes
@@ -0,0 +1,169 @@
1
+ import json
2
+ from datetime import datetime
3
+
4
+ from frogml._proto.qwak.builds.builds_pb2 import BuildStatus, ValueType
5
+ from frogml._proto.qwak.deployment.deployment_pb2 import ModelDeploymentStatus
6
+ from frogml.core.clients.build_orchestrator import BuildOrchestratorClient
7
+ from frogml.core.clients.deployment.client import DeploymentManagementClient
8
+ from frogml.core.clients.model_management import ModelsManagementClient
9
+ from google.protobuf.json_format import MessageToDict
10
+ from tabulate import tabulate
11
+
12
+
13
+ def execute_model_describe(model_id, interface, show_list_builds, output_format):
14
+ model = _model_data(model_id)
15
+ deployment_data = _get_deployment_data(model)
16
+ list_builds = _builds_data(show_list_builds, model)
17
+ schema_data = _schema_data(interface, deployment_data)
18
+ if output_format == "text":
19
+ print_text_data(model, list_builds, schema_data, deployment_data)
20
+ elif output_format == "json":
21
+ print_json_data(model, list_builds, schema_data)
22
+
23
+
24
+ def _get_deployment_data(model):
25
+ return DeploymentManagementClient().get_deployment_details(
26
+ model_id=model.model_id, model_uuid=model.uuid
27
+ )
28
+
29
+
30
+ def _model_data(model_id):
31
+ models_management = ModelsManagementClient()
32
+ return models_management.get_model(model_id)
33
+
34
+
35
+ def _builds_data(list_builds, model):
36
+ if list_builds:
37
+ builds_orchestrator = BuildOrchestratorClient()
38
+ return builds_orchestrator.list_builds(model.uuid)
39
+ return None
40
+
41
+
42
+ def _schema_data(interface, deployment_data):
43
+ if interface and deployment_data.current_deployment_details.build_id:
44
+ build_response = BuildOrchestratorClient().get_build(
45
+ deployment_data.current_deployment_details.build_id
46
+ )
47
+ if build_response.build.HasField("model_schema"):
48
+ return build_response.build.model_schema
49
+ return None
50
+
51
+
52
+ def print_json_data(model, list_builds, schema_data):
53
+ output = MessageToDict(model)
54
+ if list_builds:
55
+ output["builds"] = MessageToDict(list_builds)
56
+ if schema_data:
57
+ output["interface"] = MessageToDict(schema_data)
58
+ print(json.dumps(output, indent=4, sort_keys=True))
59
+
60
+
61
+ def print_text_data(model, list_builds, schema_data, deployment_data):
62
+ print(
63
+ f"Model id: {model.model_id}\nDisplay name: {model.display_name}\nDescription: {model.model_description}\n"
64
+ + f'Creation Date: {datetime.fromtimestamp(model.created_at.seconds + model.created_at.nanos / 1e9).strftime("%A, %B %d, %Y %I:%M:%S")}\n'
65
+ + f'Last update: {datetime.fromtimestamp(model.created_at.seconds + model.last_modified_at.nanos / 1e9).strftime("%A, %B %d, %Y %I:%M:%S")}'
66
+ )
67
+ if list_builds:
68
+ _print_text_builds(deployment_data, list_builds)
69
+ if schema_data:
70
+ _print_text_schema(schema_data)
71
+
72
+
73
+ def _print_text_schema(schema_data):
74
+ columns = [
75
+ "Parameter name",
76
+ "Parameter type",
77
+ "Parameter source",
78
+ "Parameter category",
79
+ ]
80
+ data = []
81
+ for entity in schema_data.entities:
82
+ data.append(
83
+ [
84
+ entity.name,
85
+ ValueType.Types.Name(entity.type.type),
86
+ None,
87
+ "Input",
88
+ ]
89
+ )
90
+ for feature in schema_data.features:
91
+ parsed_feature = _parse_feature(feature)
92
+ if parsed_feature:
93
+ data.append(parsed_feature)
94
+
95
+ for prediction in schema_data.predictions:
96
+ data.append(
97
+ [
98
+ prediction.name,
99
+ ValueType.Types.Name(prediction.type.type),
100
+ None,
101
+ "Output",
102
+ ]
103
+ )
104
+ print("\n" + tabulate(data, headers=columns))
105
+
106
+
107
+ def _parse_feature(feature):
108
+ if feature.HasField("explicit_feature"):
109
+ return [
110
+ feature.explicit_feature.name,
111
+ ValueType.Types.Name(feature.explicit_feature.type.type),
112
+ None,
113
+ "Input",
114
+ ]
115
+ elif feature.HasField("batch_feature"):
116
+ return [
117
+ feature.batch_feature.name,
118
+ None,
119
+ feature.batch_feature.entity.name,
120
+ "Batch Feature",
121
+ ]
122
+ elif feature.HasField("on_the_fly_feature"):
123
+ return [
124
+ feature.on_the_fly_feature.name,
125
+ None,
126
+ str(
127
+ [
128
+ source.explicit_feature.name
129
+ for source in feature.on_the_fly_feature.source_features
130
+ ]
131
+ ),
132
+ "On-The-Fly Feature",
133
+ ]
134
+
135
+
136
+ def _print_text_builds(deployment_data, list_builds):
137
+ columns = [
138
+ "Build id",
139
+ "Commit id",
140
+ "Last modified date",
141
+ "Build Status",
142
+ "Deployment build status",
143
+ ]
144
+ data = []
145
+ for build in list_builds.build:
146
+ deployments = deployment_data.build_to_environment_deployment_status.get(
147
+ build.buildId
148
+ )
149
+ if deployments:
150
+ deployment_status = ModelDeploymentStatus.Name(
151
+ number=list(deployments.environment_to_deployment_brief.values())[
152
+ 0
153
+ ].status
154
+ )
155
+ else:
156
+ deployment_status = ""
157
+ data.append(
158
+ [
159
+ build.buildId,
160
+ build.commitId,
161
+ datetime.fromtimestamp(
162
+ build.audit.last_modified_at.seconds
163
+ + build.audit.last_modified_at.nanos / 1e9
164
+ ).strftime("%A, %B %d, %Y %I:%M:%S"),
165
+ BuildStatus.Name(number=build.build_status),
166
+ deployment_status,
167
+ ]
168
+ )
169
+ print("\n" + tabulate(data, headers=columns))
@@ -0,0 +1,35 @@
1
+ import click
2
+
3
+ from frogml_cli.commands.models.describe._logic import execute_model_describe
4
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
5
+
6
+
7
+ @click.command("describe", cls=FrogMLCommand)
8
+ @click.option("--model-id", metavar="NAME", help="Model ID")
9
+ @click.option(
10
+ "--interface",
11
+ metavar="",
12
+ required=False,
13
+ default=False,
14
+ help="Returns the deployed build interface",
15
+ is_flag=True,
16
+ )
17
+ @click.option(
18
+ "--list-builds",
19
+ metavar="",
20
+ required=False,
21
+ default=False,
22
+ help="Returns the list of builds of model",
23
+ is_flag=True,
24
+ )
25
+ @click.option(
26
+ "--format",
27
+ default="text",
28
+ show_default=True,
29
+ type=click.Choice(["text", "json"], case_sensitive=True),
30
+ metavar="FORMAT",
31
+ required=False,
32
+ help="The formatting style for commands output (choose from text, json)",
33
+ )
34
+ def model_describe(model_id, interface, list_builds, format, **kwargs):
35
+ execute_model_describe(model_id, interface, list_builds, format)
File without changes
@@ -0,0 +1,9 @@
1
+ from frogml.core.clients.batch_job_management import BatchJobManagerClient
2
+ from frogml.core.clients.batch_job_management.results import CancelExecutionResult
3
+
4
+
5
+ def execute_execution_cancel(execution_id: str):
6
+ batch_job_cancel_response: CancelExecutionResult = (
7
+ BatchJobManagerClient().cancel_execution(execution_id)
8
+ )
9
+ return batch_job_cancel_response.success, batch_job_cancel_response.failure_message
@@ -0,0 +1,27 @@
1
+ import click
2
+ from frogml.core.exceptions import FrogmlException
3
+ from frogml.core.tools.logger.logger import get_frogml_logger
4
+
5
+ from frogml_cli.commands.models.executions.cancel._logic import execute_execution_cancel
6
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
7
+
8
+ logger = get_frogml_logger()
9
+
10
+
11
+ @click.command(name="cancel", help="Cancel a running execution.", cls=FrogMLCommand)
12
+ @click.option(
13
+ "--execution-id",
14
+ type=str,
15
+ help="The execution id.",
16
+ )
17
+ def execution_cancel(execution_id: str, **kwargs):
18
+ try:
19
+ success, fail_message = execute_execution_cancel(execution_id)
20
+ if not success:
21
+ raise FrogmlException(fail_message)
22
+
23
+ logger.info(f"Execution {execution_id} cancelled successfully.")
24
+
25
+ except Exception as e:
26
+ logger.error(f"Failed to cancel execution, Error: {e}")
27
+ raise
@@ -0,0 +1,24 @@
1
+ import click
2
+ from frogml.core.tools.logger.logger import get_frogml_logger
3
+
4
+ from frogml_cli.commands.models.executions.cancel.ui import execution_cancel
5
+ from frogml_cli.commands.models.executions.report.ui import execution_report
6
+ from frogml_cli.commands.models.executions.start.ui import execution_start
7
+ from frogml_cli.commands.models.executions.status.ui import execution_status
8
+
9
+ logger = get_frogml_logger()
10
+
11
+
12
+ @click.group(
13
+ name="execution",
14
+ help="Executions of a batch job on deployed model.",
15
+ )
16
+ def execution_commands_group():
17
+ # Click commands group injection
18
+ pass
19
+
20
+
21
+ execution_commands_group.add_command(execution_cancel)
22
+ execution_commands_group.add_command(execution_report)
23
+ execution_commands_group.add_command(execution_start)
24
+ execution_commands_group.add_command(execution_status)
@@ -0,0 +1,14 @@
1
+ from frogml.core.clients.batch_job_management import BatchJobManagerClient
2
+ from frogml.core.clients.batch_job_management.results import GetExecutionReportResult
3
+
4
+
5
+ def execute_execution_report(execution_id: str):
6
+ batch_job_report_response: GetExecutionReportResult = (
7
+ BatchJobManagerClient().get_execution_report(execution_id)
8
+ )
9
+ return (
10
+ batch_job_report_response.records,
11
+ batch_job_report_response.model_logs,
12
+ batch_job_report_response.success,
13
+ batch_job_report_response.failure_message,
14
+ )
@@ -0,0 +1,43 @@
1
+ import click
2
+ from frogml.core.exceptions import FrogmlException
3
+ from frogml.core.tools.logger.logger import get_frogml_logger
4
+
5
+ from frogml_cli.commands.models.executions.report._logic import execute_execution_report
6
+ from frogml_cli.inner.tools.cli_tools import FrogMLCommand
7
+ from frogml_cli.tools.colors import Color
8
+
9
+ logger = get_frogml_logger()
10
+
11
+
12
+ @click.command(name="report", help="Get the report of an execution.", cls=FrogMLCommand)
13
+ @click.option(
14
+ "--execution-id",
15
+ type=str,
16
+ help="The execution id.",
17
+ )
18
+ def execution_report(execution_id: str, **kwargs):
19
+ try:
20
+ report_messages, model_logs, success, fail_message = execute_execution_report(
21
+ execution_id
22
+ )
23
+ if not success:
24
+ raise FrogmlException(fail_message)
25
+
26
+ colored_report_lines = [
27
+ f"{Color.BLUE}{message}{Color.END}" for message in report_messages
28
+ ]
29
+
30
+ colored_log_lines = [
31
+ f"{Color.BLUE}{message}{Color.END}" for message in model_logs
32
+ ]
33
+
34
+ print(f"{Color.WHITE}Execution Report{Color.END}")
35
+ print(f"{Color.WHITE}================{Color.END}")
36
+ print("\n".join(colored_report_lines), end="\n")
37
+ print(f"{Color.WHITE}Execution Logs{Color.END}")
38
+ print(f"{Color.WHITE}=============={Color.END}")
39
+ print("\n".join(colored_log_lines), end="\n")
40
+
41
+ except Exception as e:
42
+ logger.error(f"Failed to get execution report, Error: {e}")
43
+ raise