mlrun 1.3.3__py3-none-any.whl → 1.4.0__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.

Potentially problematic release.


This version of mlrun might be problematic. Click here for more details.

Files changed (444) hide show
  1. mlrun/__init__.py +3 -3
  2. mlrun/__main__.py +79 -37
  3. mlrun/api/__init__.py +1 -1
  4. mlrun/api/api/__init__.py +1 -1
  5. mlrun/api/api/api.py +4 -4
  6. mlrun/api/api/deps.py +10 -21
  7. mlrun/api/api/endpoints/__init__.py +1 -1
  8. mlrun/api/api/endpoints/artifacts.py +64 -36
  9. mlrun/api/api/endpoints/auth.py +4 -4
  10. mlrun/api/api/endpoints/background_tasks.py +11 -11
  11. mlrun/api/api/endpoints/client_spec.py +5 -5
  12. mlrun/api/api/endpoints/clusterization_spec.py +6 -4
  13. mlrun/api/api/endpoints/feature_store.py +124 -115
  14. mlrun/api/api/endpoints/files.py +22 -14
  15. mlrun/api/api/endpoints/frontend_spec.py +28 -21
  16. mlrun/api/api/endpoints/functions.py +142 -87
  17. mlrun/api/api/endpoints/grafana_proxy.py +89 -442
  18. mlrun/api/api/endpoints/healthz.py +20 -7
  19. mlrun/api/api/endpoints/hub.py +320 -0
  20. mlrun/api/api/endpoints/internal/__init__.py +1 -1
  21. mlrun/api/api/endpoints/internal/config.py +1 -1
  22. mlrun/api/api/endpoints/internal/memory_reports.py +9 -9
  23. mlrun/api/api/endpoints/logs.py +11 -11
  24. mlrun/api/api/endpoints/model_endpoints.py +74 -70
  25. mlrun/api/api/endpoints/operations.py +13 -9
  26. mlrun/api/api/endpoints/pipelines.py +93 -88
  27. mlrun/api/api/endpoints/projects.py +35 -35
  28. mlrun/api/api/endpoints/runs.py +69 -27
  29. mlrun/api/api/endpoints/runtime_resources.py +28 -28
  30. mlrun/api/api/endpoints/schedules.py +98 -41
  31. mlrun/api/api/endpoints/secrets.py +37 -32
  32. mlrun/api/api/endpoints/submit.py +12 -12
  33. mlrun/api/api/endpoints/tags.py +20 -22
  34. mlrun/api/api/utils.py +251 -42
  35. mlrun/api/constants.py +1 -1
  36. mlrun/api/crud/__init__.py +18 -15
  37. mlrun/api/crud/artifacts.py +10 -10
  38. mlrun/api/crud/client_spec.py +4 -4
  39. mlrun/api/crud/clusterization_spec.py +3 -3
  40. mlrun/api/crud/feature_store.py +54 -46
  41. mlrun/api/crud/functions.py +3 -3
  42. mlrun/api/crud/hub.py +312 -0
  43. mlrun/api/crud/logs.py +11 -9
  44. mlrun/api/crud/model_monitoring/__init__.py +3 -3
  45. mlrun/api/crud/model_monitoring/grafana.py +435 -0
  46. mlrun/api/crud/model_monitoring/model_endpoints.py +352 -129
  47. mlrun/api/crud/notifications.py +149 -0
  48. mlrun/api/crud/pipelines.py +67 -52
  49. mlrun/api/crud/projects.py +51 -23
  50. mlrun/api/crud/runs.py +7 -5
  51. mlrun/api/crud/runtime_resources.py +13 -13
  52. mlrun/api/{db/filedb → crud/runtimes}/__init__.py +1 -1
  53. mlrun/api/crud/runtimes/nuclio/__init__.py +14 -0
  54. mlrun/api/crud/runtimes/nuclio/function.py +505 -0
  55. mlrun/api/crud/runtimes/nuclio/helpers.py +310 -0
  56. mlrun/api/crud/secrets.py +88 -46
  57. mlrun/api/crud/tags.py +5 -5
  58. mlrun/api/db/__init__.py +1 -1
  59. mlrun/api/db/base.py +102 -54
  60. mlrun/api/db/init_db.py +2 -3
  61. mlrun/api/db/session.py +4 -12
  62. mlrun/api/db/sqldb/__init__.py +1 -1
  63. mlrun/api/db/sqldb/db.py +439 -196
  64. mlrun/api/db/sqldb/helpers.py +1 -1
  65. mlrun/api/db/sqldb/models/__init__.py +3 -3
  66. mlrun/api/db/sqldb/models/models_mysql.py +82 -64
  67. mlrun/api/db/sqldb/models/models_sqlite.py +76 -64
  68. mlrun/api/db/sqldb/session.py +27 -20
  69. mlrun/api/initial_data.py +82 -24
  70. mlrun/api/launcher.py +196 -0
  71. mlrun/api/main.py +91 -22
  72. mlrun/api/middlewares.py +6 -5
  73. mlrun/api/migrations_mysql/env.py +1 -1
  74. mlrun/api/migrations_mysql/versions/28383af526f3_market_place_to_hub.py +40 -0
  75. mlrun/api/migrations_mysql/versions/32bae1b0e29c_increase_timestamp_fields_precision.py +1 -1
  76. mlrun/api/migrations_mysql/versions/4903aef6a91d_tag_foreign_key_and_cascades.py +1 -1
  77. mlrun/api/migrations_mysql/versions/5f1351c88a19_adding_background_tasks_table.py +1 -1
  78. mlrun/api/migrations_mysql/versions/88e656800d6a_add_requested_logs_column_and_index_to_.py +1 -1
  79. mlrun/api/migrations_mysql/versions/9d16de5f03a7_adding_data_versions_table.py +1 -1
  80. mlrun/api/migrations_mysql/versions/b86f5b53f3d7_adding_name_and_updated_to_runs_table.py +1 -1
  81. mlrun/api/migrations_mysql/versions/c4af40b0bf61_init.py +1 -1
  82. mlrun/api/migrations_mysql/versions/c905d15bd91d_notifications.py +72 -0
  83. mlrun/api/migrations_mysql/versions/ee041e8fdaa0_adding_next_run_time_column_to_schedule_.py +1 -1
  84. mlrun/api/migrations_sqlite/env.py +1 -1
  85. mlrun/api/migrations_sqlite/versions/11f8dd2dc9fe_init.py +1 -1
  86. mlrun/api/migrations_sqlite/versions/1c954f8cb32d_schedule_last_run_uri.py +1 -1
  87. mlrun/api/migrations_sqlite/versions/2b6d23c715aa_adding_feature_sets.py +1 -1
  88. mlrun/api/migrations_sqlite/versions/4acd9430b093_market_place_to_hub.py +77 -0
  89. mlrun/api/migrations_sqlite/versions/6401142f2d7c_adding_next_run_time_column_to_schedule_.py +1 -1
  90. mlrun/api/migrations_sqlite/versions/64d90a1a69bc_adding_background_tasks_table.py +1 -1
  91. mlrun/api/migrations_sqlite/versions/803438ecd005_add_requested_logs_column_to_runs.py +1 -1
  92. mlrun/api/migrations_sqlite/versions/863114f0c659_refactoring_feature_set.py +1 -1
  93. mlrun/api/migrations_sqlite/versions/959ae00528ad_notifications.py +63 -0
  94. mlrun/api/migrations_sqlite/versions/accf9fc83d38_adding_data_versions_table.py +1 -1
  95. mlrun/api/migrations_sqlite/versions/b68e8e897a28_schedule_labels.py +1 -1
  96. mlrun/api/migrations_sqlite/versions/bcd0c1f9720c_adding_project_labels.py +1 -1
  97. mlrun/api/migrations_sqlite/versions/cf21882f938e_schedule_id.py +1 -1
  98. mlrun/api/migrations_sqlite/versions/d781f58f607f_tag_object_name_string.py +1 -1
  99. mlrun/api/migrations_sqlite/versions/deac06871ace_adding_marketplace_sources_table.py +1 -1
  100. mlrun/api/migrations_sqlite/versions/e1dd5983c06b_schedule_concurrency_limit.py +1 -1
  101. mlrun/api/migrations_sqlite/versions/e5594ed3ab53_adding_name_and_updated_to_runs_table.py +1 -1
  102. mlrun/api/migrations_sqlite/versions/f4249b4ba6fa_adding_feature_vectors.py +1 -1
  103. mlrun/api/migrations_sqlite/versions/f7b5a1a03629_adding_feature_labels.py +1 -1
  104. mlrun/api/schemas/__init__.py +216 -138
  105. mlrun/api/utils/__init__.py +1 -1
  106. mlrun/api/utils/asyncio.py +1 -1
  107. mlrun/api/utils/auth/__init__.py +1 -1
  108. mlrun/api/utils/auth/providers/__init__.py +1 -1
  109. mlrun/api/utils/auth/providers/base.py +7 -7
  110. mlrun/api/utils/auth/providers/nop.py +6 -7
  111. mlrun/api/utils/auth/providers/opa.py +17 -17
  112. mlrun/api/utils/auth/verifier.py +36 -34
  113. mlrun/api/utils/background_tasks.py +24 -24
  114. mlrun/{builder.py → api/utils/builder.py} +216 -123
  115. mlrun/api/utils/clients/__init__.py +1 -1
  116. mlrun/api/utils/clients/chief.py +19 -4
  117. mlrun/api/utils/clients/iguazio.py +106 -60
  118. mlrun/api/utils/clients/log_collector.py +1 -1
  119. mlrun/api/utils/clients/nuclio.py +23 -23
  120. mlrun/api/utils/clients/protocols/grpc.py +2 -2
  121. mlrun/api/utils/db/__init__.py +1 -1
  122. mlrun/api/utils/db/alembic.py +1 -1
  123. mlrun/api/utils/db/backup.py +1 -1
  124. mlrun/api/utils/db/mysql.py +24 -25
  125. mlrun/api/utils/db/sql_collation.py +1 -1
  126. mlrun/api/utils/db/sqlite_migration.py +2 -2
  127. mlrun/api/utils/events/__init__.py +14 -0
  128. mlrun/api/utils/events/base.py +57 -0
  129. mlrun/api/utils/events/events_factory.py +41 -0
  130. mlrun/api/utils/events/iguazio.py +217 -0
  131. mlrun/api/utils/events/nop.py +55 -0
  132. mlrun/api/utils/helpers.py +16 -13
  133. mlrun/api/utils/memory_reports.py +1 -1
  134. mlrun/api/utils/periodic.py +6 -3
  135. mlrun/api/utils/projects/__init__.py +1 -1
  136. mlrun/api/utils/projects/follower.py +33 -33
  137. mlrun/api/utils/projects/leader.py +36 -34
  138. mlrun/api/utils/projects/member.py +27 -27
  139. mlrun/api/utils/projects/remotes/__init__.py +1 -1
  140. mlrun/api/utils/projects/remotes/follower.py +13 -13
  141. mlrun/api/utils/projects/remotes/leader.py +10 -10
  142. mlrun/api/utils/projects/remotes/nop_follower.py +27 -21
  143. mlrun/api/utils/projects/remotes/nop_leader.py +17 -16
  144. mlrun/api/utils/scheduler.py +140 -51
  145. mlrun/api/utils/singletons/__init__.py +1 -1
  146. mlrun/api/utils/singletons/db.py +9 -15
  147. mlrun/api/utils/singletons/k8s.py +677 -5
  148. mlrun/api/utils/singletons/logs_dir.py +1 -1
  149. mlrun/api/utils/singletons/project_member.py +1 -1
  150. mlrun/api/utils/singletons/scheduler.py +1 -1
  151. mlrun/artifacts/__init__.py +2 -2
  152. mlrun/artifacts/base.py +8 -2
  153. mlrun/artifacts/dataset.py +5 -3
  154. mlrun/artifacts/manager.py +7 -1
  155. mlrun/artifacts/model.py +15 -4
  156. mlrun/artifacts/plots.py +1 -1
  157. mlrun/common/__init__.py +1 -1
  158. mlrun/common/constants.py +15 -0
  159. mlrun/common/model_monitoring.py +209 -0
  160. mlrun/common/schemas/__init__.py +167 -0
  161. mlrun/{api → common}/schemas/artifact.py +13 -14
  162. mlrun/{api → common}/schemas/auth.py +10 -8
  163. mlrun/{api → common}/schemas/background_task.py +3 -3
  164. mlrun/{api → common}/schemas/client_spec.py +1 -1
  165. mlrun/{api → common}/schemas/clusterization_spec.py +3 -3
  166. mlrun/{api → common}/schemas/constants.py +21 -8
  167. mlrun/common/schemas/events.py +36 -0
  168. mlrun/{api → common}/schemas/feature_store.py +2 -1
  169. mlrun/{api → common}/schemas/frontend_spec.py +7 -6
  170. mlrun/{api → common}/schemas/function.py +5 -5
  171. mlrun/{api → common}/schemas/http.py +3 -3
  172. mlrun/common/schemas/hub.py +134 -0
  173. mlrun/{api → common}/schemas/k8s.py +3 -3
  174. mlrun/{api → common}/schemas/memory_reports.py +1 -1
  175. mlrun/common/schemas/model_endpoints.py +342 -0
  176. mlrun/common/schemas/notification.py +57 -0
  177. mlrun/{api → common}/schemas/object.py +6 -6
  178. mlrun/{api → common}/schemas/pipeline.py +3 -3
  179. mlrun/{api → common}/schemas/project.py +6 -5
  180. mlrun/common/schemas/regex.py +24 -0
  181. mlrun/common/schemas/runs.py +30 -0
  182. mlrun/{api → common}/schemas/runtime_resource.py +3 -3
  183. mlrun/{api → common}/schemas/schedule.py +19 -7
  184. mlrun/{api → common}/schemas/secret.py +3 -3
  185. mlrun/{api → common}/schemas/tag.py +2 -2
  186. mlrun/common/types.py +25 -0
  187. mlrun/config.py +152 -20
  188. mlrun/data_types/__init__.py +7 -2
  189. mlrun/data_types/data_types.py +4 -2
  190. mlrun/data_types/infer.py +1 -1
  191. mlrun/data_types/spark.py +10 -3
  192. mlrun/datastore/__init__.py +10 -3
  193. mlrun/datastore/azure_blob.py +1 -1
  194. mlrun/datastore/base.py +185 -53
  195. mlrun/datastore/datastore.py +1 -1
  196. mlrun/datastore/filestore.py +1 -1
  197. mlrun/datastore/google_cloud_storage.py +1 -1
  198. mlrun/datastore/inmem.py +4 -1
  199. mlrun/datastore/redis.py +1 -1
  200. mlrun/datastore/s3.py +1 -1
  201. mlrun/datastore/sources.py +192 -70
  202. mlrun/datastore/spark_udf.py +44 -0
  203. mlrun/datastore/store_resources.py +4 -4
  204. mlrun/datastore/targets.py +115 -45
  205. mlrun/datastore/utils.py +127 -5
  206. mlrun/datastore/v3io.py +1 -1
  207. mlrun/datastore/wasbfs/__init__.py +1 -1
  208. mlrun/datastore/wasbfs/fs.py +1 -1
  209. mlrun/db/__init__.py +7 -5
  210. mlrun/db/base.py +112 -68
  211. mlrun/db/httpdb.py +445 -277
  212. mlrun/db/nopdb.py +491 -0
  213. mlrun/db/sqldb.py +112 -65
  214. mlrun/errors.py +6 -1
  215. mlrun/execution.py +44 -22
  216. mlrun/feature_store/__init__.py +1 -1
  217. mlrun/feature_store/api.py +143 -95
  218. mlrun/feature_store/common.py +16 -20
  219. mlrun/feature_store/feature_set.py +42 -12
  220. mlrun/feature_store/feature_vector.py +32 -21
  221. mlrun/feature_store/ingestion.py +9 -12
  222. mlrun/feature_store/retrieval/__init__.py +3 -2
  223. mlrun/feature_store/retrieval/base.py +388 -66
  224. mlrun/feature_store/retrieval/dask_merger.py +63 -151
  225. mlrun/feature_store/retrieval/job.py +30 -12
  226. mlrun/feature_store/retrieval/local_merger.py +40 -133
  227. mlrun/feature_store/retrieval/spark_merger.py +129 -127
  228. mlrun/feature_store/retrieval/storey_merger.py +173 -0
  229. mlrun/feature_store/steps.py +132 -15
  230. mlrun/features.py +8 -3
  231. mlrun/frameworks/__init__.py +1 -1
  232. mlrun/frameworks/_common/__init__.py +1 -1
  233. mlrun/frameworks/_common/artifacts_library.py +1 -1
  234. mlrun/frameworks/_common/mlrun_interface.py +1 -1
  235. mlrun/frameworks/_common/model_handler.py +1 -1
  236. mlrun/frameworks/_common/plan.py +1 -1
  237. mlrun/frameworks/_common/producer.py +1 -1
  238. mlrun/frameworks/_common/utils.py +1 -1
  239. mlrun/frameworks/_dl_common/__init__.py +1 -1
  240. mlrun/frameworks/_dl_common/loggers/__init__.py +1 -1
  241. mlrun/frameworks/_dl_common/loggers/logger.py +1 -1
  242. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +1 -1
  243. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +1 -1
  244. mlrun/frameworks/_dl_common/model_handler.py +1 -1
  245. mlrun/frameworks/_dl_common/utils.py +1 -1
  246. mlrun/frameworks/_ml_common/__init__.py +1 -1
  247. mlrun/frameworks/_ml_common/artifacts_library.py +1 -1
  248. mlrun/frameworks/_ml_common/loggers/__init__.py +1 -1
  249. mlrun/frameworks/_ml_common/loggers/logger.py +1 -1
  250. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +1 -1
  251. mlrun/frameworks/_ml_common/model_handler.py +1 -1
  252. mlrun/frameworks/_ml_common/pkl_model_server.py +13 -1
  253. mlrun/frameworks/_ml_common/plan.py +1 -1
  254. mlrun/frameworks/_ml_common/plans/__init__.py +1 -1
  255. mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +1 -6
  256. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +1 -1
  257. mlrun/frameworks/_ml_common/plans/dataset_plan.py +1 -1
  258. mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +1 -1
  259. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +1 -1
  260. mlrun/frameworks/_ml_common/producer.py +1 -1
  261. mlrun/frameworks/_ml_common/utils.py +1 -1
  262. mlrun/frameworks/auto_mlrun/__init__.py +1 -1
  263. mlrun/frameworks/auto_mlrun/auto_mlrun.py +1 -1
  264. mlrun/frameworks/huggingface/__init__.py +1 -1
  265. mlrun/frameworks/huggingface/model_server.py +1 -1
  266. mlrun/frameworks/lgbm/__init__.py +1 -1
  267. mlrun/frameworks/lgbm/callbacks/__init__.py +1 -1
  268. mlrun/frameworks/lgbm/callbacks/callback.py +1 -1
  269. mlrun/frameworks/lgbm/callbacks/logging_callback.py +1 -1
  270. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +1 -1
  271. mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -1
  272. mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -1
  273. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +1 -1
  274. mlrun/frameworks/lgbm/mlrun_interfaces/model_mlrun_interface.py +1 -1
  275. mlrun/frameworks/lgbm/model_handler.py +1 -1
  276. mlrun/frameworks/lgbm/model_server.py +1 -1
  277. mlrun/frameworks/lgbm/utils.py +1 -1
  278. mlrun/frameworks/onnx/__init__.py +1 -1
  279. mlrun/frameworks/onnx/dataset.py +1 -1
  280. mlrun/frameworks/onnx/mlrun_interface.py +1 -1
  281. mlrun/frameworks/onnx/model_handler.py +1 -1
  282. mlrun/frameworks/onnx/model_server.py +1 -1
  283. mlrun/frameworks/parallel_coordinates.py +1 -1
  284. mlrun/frameworks/pytorch/__init__.py +1 -1
  285. mlrun/frameworks/pytorch/callbacks/__init__.py +1 -1
  286. mlrun/frameworks/pytorch/callbacks/callback.py +1 -1
  287. mlrun/frameworks/pytorch/callbacks/logging_callback.py +1 -1
  288. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +1 -1
  289. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +1 -1
  290. mlrun/frameworks/pytorch/callbacks_handler.py +1 -1
  291. mlrun/frameworks/pytorch/mlrun_interface.py +1 -1
  292. mlrun/frameworks/pytorch/model_handler.py +1 -1
  293. mlrun/frameworks/pytorch/model_server.py +1 -1
  294. mlrun/frameworks/pytorch/utils.py +1 -1
  295. mlrun/frameworks/sklearn/__init__.py +1 -1
  296. mlrun/frameworks/sklearn/estimator.py +1 -1
  297. mlrun/frameworks/sklearn/metric.py +1 -1
  298. mlrun/frameworks/sklearn/metrics_library.py +1 -1
  299. mlrun/frameworks/sklearn/mlrun_interface.py +1 -1
  300. mlrun/frameworks/sklearn/model_handler.py +1 -1
  301. mlrun/frameworks/sklearn/utils.py +1 -1
  302. mlrun/frameworks/tf_keras/__init__.py +1 -1
  303. mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -1
  304. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  305. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +1 -1
  306. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +1 -1
  307. mlrun/frameworks/tf_keras/mlrun_interface.py +1 -1
  308. mlrun/frameworks/tf_keras/model_handler.py +1 -1
  309. mlrun/frameworks/tf_keras/model_server.py +1 -1
  310. mlrun/frameworks/tf_keras/utils.py +1 -1
  311. mlrun/frameworks/xgboost/__init__.py +1 -1
  312. mlrun/frameworks/xgboost/mlrun_interface.py +1 -1
  313. mlrun/frameworks/xgboost/model_handler.py +1 -1
  314. mlrun/frameworks/xgboost/utils.py +1 -1
  315. mlrun/k8s_utils.py +14 -765
  316. mlrun/kfpops.py +14 -17
  317. mlrun/launcher/__init__.py +13 -0
  318. mlrun/launcher/base.py +406 -0
  319. mlrun/launcher/client.py +159 -0
  320. mlrun/launcher/factory.py +50 -0
  321. mlrun/launcher/local.py +276 -0
  322. mlrun/launcher/remote.py +178 -0
  323. mlrun/lists.py +10 -2
  324. mlrun/mlutils/__init__.py +1 -1
  325. mlrun/mlutils/data.py +1 -1
  326. mlrun/mlutils/models.py +1 -1
  327. mlrun/mlutils/plots.py +1 -1
  328. mlrun/model.py +252 -14
  329. mlrun/model_monitoring/__init__.py +41 -0
  330. mlrun/model_monitoring/features_drift_table.py +1 -1
  331. mlrun/model_monitoring/helpers.py +123 -38
  332. mlrun/model_monitoring/model_endpoint.py +144 -0
  333. mlrun/model_monitoring/model_monitoring_batch.py +310 -259
  334. mlrun/model_monitoring/stores/__init__.py +106 -0
  335. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +448 -0
  336. mlrun/model_monitoring/stores/model_endpoint_store.py +147 -0
  337. mlrun/model_monitoring/stores/models/__init__.py +23 -0
  338. mlrun/model_monitoring/stores/models/base.py +18 -0
  339. mlrun/model_monitoring/stores/models/mysql.py +100 -0
  340. mlrun/model_monitoring/stores/models/sqlite.py +98 -0
  341. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +370 -0
  342. mlrun/model_monitoring/stream_processing_fs.py +239 -271
  343. mlrun/package/__init__.py +163 -0
  344. mlrun/package/context_handler.py +325 -0
  345. mlrun/package/errors.py +47 -0
  346. mlrun/package/packager.py +298 -0
  347. mlrun/{runtimes/package → package/packagers}/__init__.py +3 -1
  348. mlrun/package/packagers/default_packager.py +422 -0
  349. mlrun/package/packagers/numpy_packagers.py +612 -0
  350. mlrun/package/packagers/pandas_packagers.py +968 -0
  351. mlrun/package/packagers/python_standard_library_packagers.py +616 -0
  352. mlrun/package/packagers_manager.py +786 -0
  353. mlrun/package/utils/__init__.py +53 -0
  354. mlrun/package/utils/_archiver.py +226 -0
  355. mlrun/package/utils/_formatter.py +211 -0
  356. mlrun/package/utils/_pickler.py +234 -0
  357. mlrun/package/utils/_supported_format.py +71 -0
  358. mlrun/package/utils/log_hint_utils.py +93 -0
  359. mlrun/package/utils/type_hint_utils.py +298 -0
  360. mlrun/platforms/__init__.py +1 -1
  361. mlrun/platforms/iguazio.py +34 -2
  362. mlrun/platforms/other.py +1 -1
  363. mlrun/projects/__init__.py +1 -1
  364. mlrun/projects/operations.py +14 -9
  365. mlrun/projects/pipelines.py +31 -13
  366. mlrun/projects/project.py +762 -238
  367. mlrun/render.py +49 -19
  368. mlrun/run.py +57 -326
  369. mlrun/runtimes/__init__.py +3 -9
  370. mlrun/runtimes/base.py +247 -784
  371. mlrun/runtimes/constants.py +1 -1
  372. mlrun/runtimes/daskjob.py +45 -41
  373. mlrun/runtimes/funcdoc.py +43 -7
  374. mlrun/runtimes/function.py +66 -656
  375. mlrun/runtimes/function_reference.py +1 -1
  376. mlrun/runtimes/generators.py +1 -1
  377. mlrun/runtimes/kubejob.py +99 -116
  378. mlrun/runtimes/local.py +59 -66
  379. mlrun/runtimes/mpijob/__init__.py +1 -1
  380. mlrun/runtimes/mpijob/abstract.py +13 -15
  381. mlrun/runtimes/mpijob/v1.py +3 -1
  382. mlrun/runtimes/mpijob/v1alpha1.py +1 -1
  383. mlrun/runtimes/nuclio.py +1 -1
  384. mlrun/runtimes/pod.py +51 -26
  385. mlrun/runtimes/remotesparkjob.py +3 -1
  386. mlrun/runtimes/serving.py +12 -4
  387. mlrun/runtimes/sparkjob/__init__.py +1 -2
  388. mlrun/runtimes/sparkjob/abstract.py +44 -31
  389. mlrun/runtimes/sparkjob/spark3job.py +11 -9
  390. mlrun/runtimes/utils.py +61 -42
  391. mlrun/secrets.py +16 -18
  392. mlrun/serving/__init__.py +3 -2
  393. mlrun/serving/merger.py +1 -1
  394. mlrun/serving/remote.py +1 -1
  395. mlrun/serving/routers.py +39 -42
  396. mlrun/serving/server.py +23 -13
  397. mlrun/serving/serving_wrapper.py +1 -1
  398. mlrun/serving/states.py +172 -39
  399. mlrun/serving/utils.py +1 -1
  400. mlrun/serving/v1_serving.py +1 -1
  401. mlrun/serving/v2_serving.py +29 -21
  402. mlrun/utils/__init__.py +1 -2
  403. mlrun/utils/async_http.py +8 -1
  404. mlrun/utils/azure_vault.py +1 -1
  405. mlrun/utils/clones.py +2 -2
  406. mlrun/utils/condition_evaluator.py +65 -0
  407. mlrun/utils/db.py +52 -0
  408. mlrun/utils/helpers.py +188 -13
  409. mlrun/utils/http.py +89 -54
  410. mlrun/utils/logger.py +48 -8
  411. mlrun/utils/model_monitoring.py +132 -100
  412. mlrun/utils/notifications/__init__.py +1 -1
  413. mlrun/utils/notifications/notification/__init__.py +8 -6
  414. mlrun/utils/notifications/notification/base.py +20 -14
  415. mlrun/utils/notifications/notification/console.py +7 -4
  416. mlrun/utils/notifications/notification/git.py +36 -19
  417. mlrun/utils/notifications/notification/ipython.py +10 -8
  418. mlrun/utils/notifications/notification/slack.py +18 -13
  419. mlrun/utils/notifications/notification_pusher.py +377 -56
  420. mlrun/utils/regex.py +6 -1
  421. mlrun/utils/singleton.py +1 -1
  422. mlrun/utils/v3io_clients.py +1 -1
  423. mlrun/utils/vault.py +270 -269
  424. mlrun/utils/version/__init__.py +1 -1
  425. mlrun/utils/version/version.json +2 -2
  426. mlrun/utils/version/version.py +1 -1
  427. {mlrun-1.3.3.dist-info → mlrun-1.4.0.dist-info}/METADATA +16 -10
  428. mlrun-1.4.0.dist-info/RECORD +434 -0
  429. mlrun/api/api/endpoints/marketplace.py +0 -257
  430. mlrun/api/crud/marketplace.py +0 -221
  431. mlrun/api/crud/model_monitoring/model_endpoint_store.py +0 -847
  432. mlrun/api/db/filedb/db.py +0 -518
  433. mlrun/api/schemas/marketplace.py +0 -128
  434. mlrun/api/schemas/model_endpoints.py +0 -185
  435. mlrun/db/filedb.py +0 -891
  436. mlrun/feature_store/retrieval/online.py +0 -92
  437. mlrun/model_monitoring/constants.py +0 -67
  438. mlrun/runtimes/package/context_handler.py +0 -711
  439. mlrun/runtimes/sparkjob/spark2job.py +0 -59
  440. mlrun-1.3.3.dist-info/RECORD +0 -381
  441. {mlrun-1.3.3.dist-info → mlrun-1.4.0.dist-info}/LICENSE +0 -0
  442. {mlrun-1.3.3.dist-info → mlrun-1.4.0.dist-info}/WHEEL +0 -0
  443. {mlrun-1.3.3.dist-info → mlrun-1.4.0.dist-info}/entry_points.txt +0 -0
  444. {mlrun-1.3.3.dist-info → mlrun-1.4.0.dist-info}/top_level.txt +0 -0
mlrun/utils/helpers.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2018 Iguazio
1
+ # Copyright 2023 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@ import enum
16
16
  import hashlib
17
17
  import inspect
18
18
  import json
19
+ import os
20
+ import pathlib
19
21
  import re
20
22
  import sys
21
23
  import time
@@ -38,6 +40,7 @@ from pandas._libs.tslibs.timestamps import Timedelta, Timestamp
38
40
  from yaml.representer import RepresenterError
39
41
 
40
42
  import mlrun
43
+ import mlrun.common.schemas
41
44
  import mlrun.errors
42
45
  import mlrun.utils.version.version
43
46
  from mlrun.errors import err_to_str
@@ -96,8 +99,11 @@ def get_artifact_target(item: dict, project=None):
96
99
  tree = item["metadata"].get("tree")
97
100
 
98
101
  kind = item.get("kind")
99
- if kind in ["dataset", "model"] and db_key:
100
- return f"{DB_SCHEMA}://{StorePrefix.Artifact}/{project_str}/{db_key}:{tree}"
102
+ if kind in ["dataset", "model", "artifact"] and db_key:
103
+ target = f"{DB_SCHEMA}://{StorePrefix.Artifact}/{project_str}/{db_key}"
104
+ if tree:
105
+ target = f"{target}:{tree}"
106
+ return target
101
107
 
102
108
  return (
103
109
  item.get("target_path")
@@ -144,6 +150,7 @@ def verify_field_regex(
144
150
  patterns,
145
151
  raise_on_failure: bool = True,
146
152
  log_message: str = "Field is malformed. Does not match required pattern",
153
+ mode: mlrun.common.schemas.RegexMatchModes = mlrun.common.schemas.RegexMatchModes.all,
147
154
  ) -> bool:
148
155
  for pattern in patterns:
149
156
  if not re.match(pattern, str(field_value)):
@@ -154,13 +161,52 @@ def verify_field_regex(
154
161
  field_value=field_value,
155
162
  pattern=pattern,
156
163
  )
157
- if raise_on_failure:
158
- raise mlrun.errors.MLRunInvalidArgumentError(
159
- f"Field '{field_name}' is malformed. Does not match required pattern: {pattern}"
160
- )
161
- else:
164
+ if mode == mlrun.common.schemas.RegexMatchModes.all:
165
+ if raise_on_failure:
166
+ raise mlrun.errors.MLRunInvalidArgumentError(
167
+ f"Field '{field_name}' is malformed. {field_value} does not match required pattern: {pattern}"
168
+ )
162
169
  return False
163
- return True
170
+ elif mode == mlrun.common.schemas.RegexMatchModes.any:
171
+ return True
172
+ if mode == mlrun.common.schemas.RegexMatchModes.all:
173
+ return True
174
+ elif mode == mlrun.common.schemas.RegexMatchModes.any:
175
+ if raise_on_failure:
176
+ raise mlrun.errors.MLRunInvalidArgumentError(
177
+ f"Field '{field_name}' is malformed. {field_value} does not match any of the"
178
+ f" required patterns: {patterns}"
179
+ )
180
+ return False
181
+
182
+
183
+ def validate_builder_source(
184
+ source: str, pull_at_runtime: bool = False, workdir: str = None
185
+ ):
186
+ if pull_at_runtime or not source:
187
+ return
188
+
189
+ if "://" not in source:
190
+ if not path.isabs(source):
191
+ raise mlrun.errors.MLRunInvalidArgumentError(
192
+ f"Source '{source}' must be a valid URL or absolute path when 'pull_at_runtime' is False "
193
+ "set 'source' to a remote URL to clone/copy the source to the base image, "
194
+ "or set 'pull_at_runtime' to True to pull the source at runtime."
195
+ )
196
+
197
+ else:
198
+ logger.warn(
199
+ "Loading local source at build time requires the source to be on the base image, "
200
+ "in which case it is recommended to use 'workdir' instead",
201
+ source=source,
202
+ workdir=workdir,
203
+ )
204
+
205
+ if source.endswith(".zip"):
206
+ logger.warn(
207
+ "zip files are not natively extracted by docker, use tar.gz for faster loading during build",
208
+ source=source,
209
+ )
164
210
 
165
211
 
166
212
  def validate_tag_name(
@@ -200,6 +246,34 @@ def get_regex_list_as_string(regex_list: List) -> str:
200
246
  return "".join(["(?={regex})".format(regex=regex) for regex in regex_list]) + ".*$"
201
247
 
202
248
 
249
+ def is_file_path_invalid(code_path: str, file_path: str) -> bool:
250
+ """
251
+ The function checks if the given file_path is a valid path.
252
+ If the file_path is a relative path, it is completed by joining it with the code_path.
253
+ Otherwise, the file_path is used as is.
254
+ Additionally, it checks if the resulting path exists as a file, unless the file_path is a remote URL.
255
+ If the file_path has no suffix, it is considered invalid.
256
+
257
+ :param code_path: The base directory or code path to search for the file in case of relative file_path
258
+ :param file_path: The file path to be validated
259
+ :return: True if the file path is invalid, False otherwise
260
+ """
261
+ if not file_path:
262
+ return True
263
+
264
+ if file_path.startswith("./") or (
265
+ "://" not in file_path and os.path.basename(file_path) == file_path
266
+ ):
267
+ abs_path = os.path.join(code_path, file_path.lstrip("./"))
268
+ else:
269
+ abs_path = file_path
270
+
271
+ return (
272
+ not (os.path.isfile(abs_path) or "://" in file_path)
273
+ or not pathlib.Path(file_path).suffix
274
+ )
275
+
276
+
203
277
  def tag_name_regex_as_string() -> str:
204
278
  return get_regex_list_as_string(mlrun.utils.regex.tag_name)
205
279
 
@@ -208,6 +282,23 @@ def is_yaml_path(url):
208
282
  return url.endswith(".yaml") or url.endswith(".yml")
209
283
 
210
284
 
285
+ def remove_image_protocol_prefix(image: str) -> str:
286
+ if not image:
287
+ return image
288
+
289
+ prefixes = ["https://", "https://"]
290
+ if any(prefix in image for prefix in prefixes):
291
+ image = image.removeprefix("https://").removeprefix("http://")
292
+ logger.warning(
293
+ "The image has an unexpected protocol prefix ('http://' or 'https://'). "
294
+ "If you wish to use the default configured registry, no protocol prefix is required "
295
+ "(note that you can also use '.<image-name>' instead of the full URL where <image-name> is a placeholder). "
296
+ "Removing protocol prefix from image.",
297
+ image=image,
298
+ )
299
+ return image
300
+
301
+
211
302
  # Verifying that a field input is of the expected type. If not the method raises a detailed MLRunInvalidArgumentError
212
303
  def verify_field_of_type(field_name: str, field_value, expected_type: type):
213
304
  if not isinstance(field_value, expected_type):
@@ -954,7 +1045,7 @@ def retry_until_successful(
954
1045
  f" last_exception: {last_exception},"
955
1046
  f" function_name: {_function.__name__},"
956
1047
  f" timeout: {timeout}"
957
- )
1048
+ ) from last_exception
958
1049
 
959
1050
 
960
1051
  def get_ui_url(project, uid=None):
@@ -1000,7 +1091,7 @@ def create_class(pkg_class: str):
1000
1091
  return class_
1001
1092
 
1002
1093
 
1003
- def create_function(pkg_func: list):
1094
+ def create_function(pkg_func: str):
1004
1095
  """Create a function from a package.module.function string
1005
1096
 
1006
1097
  :param pkg_func: full function location,
@@ -1014,9 +1105,16 @@ def create_function(pkg_func: list):
1014
1105
  return function_
1015
1106
 
1016
1107
 
1017
- def get_caller_globals(level=2):
1108
+ def get_caller_globals():
1109
+ """Returns a dictionary containing the first non-mlrun caller function's namespace."""
1018
1110
  try:
1019
- return inspect.stack()[level][0].f_globals
1111
+ stack = inspect.stack()
1112
+ # If an API function called this function directly, the first non-mlrun caller will be 2 levels up the stack.
1113
+ # Otherwise, we keep going up the stack until we find it.
1114
+ for level in range(2, len(stack)):
1115
+ namespace = stack[level][0].f_globals
1116
+ if not namespace["__name__"].startswith("mlrun."):
1117
+ return namespace
1020
1118
  except Exception:
1021
1119
  return None
1022
1120
 
@@ -1209,6 +1307,43 @@ def is_legacy_artifact(artifact):
1209
1307
  return not hasattr(artifact, "metadata")
1210
1308
 
1211
1309
 
1310
+ def format_run(run: dict, with_project=False) -> dict:
1311
+ fields = [
1312
+ "id",
1313
+ "name",
1314
+ "status",
1315
+ "error",
1316
+ "created_at",
1317
+ "scheduled_at",
1318
+ "finished_at",
1319
+ "description",
1320
+ ]
1321
+
1322
+ if with_project:
1323
+ fields.append("project")
1324
+
1325
+ # create a run object that contains all fields,
1326
+ run = {
1327
+ key: str(value) if value is not None else value
1328
+ for key, value in run.items()
1329
+ if key in fields
1330
+ }
1331
+
1332
+ # if the time_keys values is from 1970, this indicates that the field has not yet been specified yet,
1333
+ # and we want to return a None value instead
1334
+ time_keys = ["scheduled_at", "finished_at", "created_at"]
1335
+
1336
+ for key, value in run.items():
1337
+ if (
1338
+ key in time_keys
1339
+ and isinstance(value, (str, datetime))
1340
+ and parser.parse(str(value)).year == 1970
1341
+ ):
1342
+ run[key] = None
1343
+
1344
+ return run
1345
+
1346
+
1212
1347
  def get_in_artifact(artifact: dict, key, default=None, raise_on_missing=False):
1213
1348
  """artifact can be dict or Artifact object"""
1214
1349
  if is_legacy_artifact(artifact):
@@ -1245,6 +1380,18 @@ def is_relative_path(path):
1245
1380
  return not (path.startswith("/") or ":\\" in path or "://" in path)
1246
1381
 
1247
1382
 
1383
+ def is_running_in_jupyter_notebook() -> bool:
1384
+ """
1385
+ Check if the code is running inside a Jupyter Notebook.
1386
+ :return: True if running inside a Jupyter Notebook, False otherwise.
1387
+ """
1388
+ import IPython
1389
+
1390
+ ipy = IPython.get_ipython()
1391
+ # if its IPython terminal, it isn't a Jupyter ipython
1392
+ return ipy and "Terminal" not in str(type(ipy))
1393
+
1394
+
1248
1395
  def as_number(field_name, field_value):
1249
1396
  if isinstance(field_value, str) and not field_value.isnumeric():
1250
1397
  raise ValueError(f"{field_name} must be numeric (str/int types)")
@@ -1295,3 +1442,31 @@ def ensure_git_branch(url: str, repo: git.Repo) -> str:
1295
1442
  if not branch and not reference:
1296
1443
  url = f"{url}#refs/heads/{repo.active_branch}"
1297
1444
  return url
1445
+
1446
+
1447
+ def is_file_path(filepath):
1448
+ root, ext = os.path.splitext(filepath)
1449
+ return os.path.isfile(filepath) and ext
1450
+
1451
+
1452
+ class DeprecationHelper(object):
1453
+ """A helper class to deprecate old schemas"""
1454
+
1455
+ def __init__(self, new_target, version="1.4.0"):
1456
+ self._new_target = new_target
1457
+ self._version = version
1458
+
1459
+ def _warn(self):
1460
+ warnings.warn(
1461
+ f"mlrun.api.schemas.{self._new_target.__name__} is deprecated in version {self._version}, "
1462
+ f"Please use mlrun.common.schemas.{self._new_target.__name__} instead.",
1463
+ FutureWarning,
1464
+ )
1465
+
1466
+ def __call__(self, *args, **kwargs):
1467
+ self._warn()
1468
+ return self._new_target(*args, **kwargs)
1469
+
1470
+ def __getattr__(self, attr):
1471
+ self._warn()
1472
+ return getattr(self._new_target, attr)
mlrun/utils/http.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2018 Iguazio
1
+ # Copyright 2023 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,10 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+
15
16
  import time
17
+ import typing
16
18
 
17
19
  import requests
18
20
  import requests.adapters
21
+ import requests.utils
19
22
  import urllib3.exceptions
20
23
  import urllib3.util.retry
21
24
 
@@ -66,12 +69,12 @@ class HTTPSessionWithRetry(requests.Session):
66
69
  ):
67
70
  """
68
71
  Initialize a new HTTP session with retry logic.
69
- :param max_retries: Maximum number of retries to attempt.
70
- :param retry_backoff_factor: Wait interval retries in seconds.
71
- :param retry_on_exception: Retry on the HTTP_RETRYABLE_EXCEPTIONS. defaults to True.
72
- :param retry_on_status: Retry on error status codes. defaults to True.
73
- :param retry_on_post: Retry on POST requests. defaults to False.
74
- :param verbose: Print debug messages.
72
+ :param max_retries: Maximum number of retries to attempt.
73
+ :param retry_backoff_factor: Wait interval retries in seconds.
74
+ :param retry_on_exception: Retry on the HTTP_RETRYABLE_EXCEPTIONS. defaults to True.
75
+ :param retry_on_status: Retry on error status codes. defaults to True.
76
+ :param retry_on_post: Retry on POST requests. defaults to False.
77
+ :param verbose: Print debug messages.
75
78
  """
76
79
  super().__init__()
77
80
 
@@ -79,14 +82,16 @@ class HTTPSessionWithRetry(requests.Session):
79
82
  self.retry_backoff_factor = retry_backoff_factor
80
83
  self.retry_on_exception = retry_on_exception
81
84
  self.verbose = verbose
85
+ self._logger = logger.get_child("http-client")
86
+ self._retry_methods = self._resolve_retry_methods(retry_on_post)
82
87
 
83
88
  if retry_on_status:
84
- http_adapter = requests.adapters.HTTPAdapter(
89
+ self._http_adapter = requests.adapters.HTTPAdapter(
85
90
  max_retries=urllib3.util.retry.Retry(
86
91
  total=self.max_retries,
87
92
  backoff_factor=self.retry_backoff_factor,
88
93
  status_forcelist=config.http_retry_defaults.status_codes,
89
- method_whitelist=self._get_retry_methods(retry_on_post),
94
+ method_whitelist=self._retry_methods,
90
95
  # we want to retry but not to raise since we do want that last response (to parse details on the
91
96
  # error from response body) we'll handle raising ourselves
92
97
  raise_on_status=False,
@@ -94,54 +99,30 @@ class HTTPSessionWithRetry(requests.Session):
94
99
  pool_maxsize=int(config.httpdb.max_workers),
95
100
  )
96
101
 
97
- self.mount("http://", http_adapter)
98
- self.mount("https://", http_adapter)
102
+ self.mount("http://", self._http_adapter)
103
+ self.mount("https://", self._http_adapter)
99
104
 
100
105
  def request(self, method, url, **kwargs):
101
106
  retry_count = 0
107
+ kwargs.setdefault("headers", {})
108
+ kwargs["headers"][
109
+ "User-Agent"
110
+ ] = f"{requests.utils.default_user_agent()} mlrun/{config.version}"
102
111
  while True:
103
112
  try:
104
113
  response = super().request(method, url, **kwargs)
105
114
  return response
106
115
  except Exception as exc:
107
- if not self.retry_on_exception:
108
- self._log_exception(
109
- "warning",
110
- exc,
111
- f"{method} {url} request failed, http retries disabled,"
112
- f" raising exception: {err_to_str(exc)}",
113
- retry_count,
114
- )
116
+ if not self._error_is_retryable(url, method, exc, retry_count):
115
117
  raise exc
116
118
 
117
- if retry_count >= self.max_retries:
118
- self._log_exception(
119
- "warning",
120
- exc,
121
- f"{method} {url} request failed, max retries reached,"
122
- f" raising exception: {err_to_str(exc)}",
123
- retry_count,
124
- )
125
- raise exc
126
-
127
- # only retryable exceptions
128
- exception_is_retryable = any(
129
- msg in str(exc) for msg in self.HTTP_RETRYABLE_EXCEPTION_STRINGS
130
- ) or any(
131
- isinstance(exc, retryable_exc)
132
- for retryable_exc in self.HTTP_RETRYABLE_EXCEPTIONS
119
+ self._logger.warning(
120
+ "Error during request handling, retrying",
121
+ exc=str(exc),
122
+ retry_count=retry_count,
123
+ url=url,
124
+ method=method,
133
125
  )
134
-
135
- if not exception_is_retryable:
136
- self._log_exception(
137
- "warning",
138
- exc,
139
- f"{method} {url} request failed on non-retryable exception,"
140
- f" raising exception: {err_to_str(exc)}",
141
- retry_count,
142
- )
143
- raise exc
144
-
145
126
  if self.verbose:
146
127
  self._log_exception(
147
128
  "debug",
@@ -153,17 +134,71 @@ class HTTPSessionWithRetry(requests.Session):
153
134
  retry_count += 1
154
135
  time.sleep(self.retry_backoff_factor)
155
136
 
156
- @staticmethod
157
- def _get_retry_methods(retry_on_post=False):
158
- return (
159
- # setting to False in order to retry on all methods, otherwise every method except POST.
160
- False
161
- if retry_on_post
162
- else urllib3.util.retry.Retry.DEFAULT_METHOD_WHITELIST
137
+ def _error_is_retryable(self, url, method, exc, retry_count):
138
+ if not self.retry_on_exception:
139
+ self._log_exception(
140
+ "warning",
141
+ exc,
142
+ f"{method} {url} request failed, http retries disabled,"
143
+ f" raising exception: {err_to_str(exc)}",
144
+ retry_count,
145
+ )
146
+ return False
147
+
148
+ # if the response is not retryable, stop retrying
149
+ # this is done to prevent the retry logic from running on non-idempotent methods (such as POST).
150
+ if not self._method_retryable(method):
151
+ self._log_exception(
152
+ "warning",
153
+ exc,
154
+ f"{method} {url} request failed, http retries disabled for {method} method.",
155
+ retry_count,
156
+ )
157
+ return False
158
+
159
+ if retry_count >= self.max_retries:
160
+ self._log_exception(
161
+ "warning",
162
+ exc,
163
+ f"{method} {url} request failed, max retries reached,"
164
+ f" raising exception: {err_to_str(exc)}",
165
+ retry_count,
166
+ )
167
+ return False
168
+
169
+ # only retryable exceptions
170
+ exception_is_retryable = any(
171
+ msg in str(exc) for msg in self.HTTP_RETRYABLE_EXCEPTION_STRINGS
172
+ ) or any(
173
+ isinstance(exc, retryable_exc)
174
+ for retryable_exc in self.HTTP_RETRYABLE_EXCEPTIONS
163
175
  )
164
176
 
177
+ if not exception_is_retryable:
178
+ self._log_exception(
179
+ "warning",
180
+ exc,
181
+ f"{method} {url} request failed on non-retryable exception,"
182
+ f" raising exception: {err_to_str(exc)}",
183
+ retry_count,
184
+ )
185
+ return False
186
+ return True
187
+
188
+ def _method_retryable(self, method: str):
189
+ return method in self._retry_methods
190
+
191
+ def _resolve_retry_methods(
192
+ self, retry_on_post: bool = False
193
+ ) -> typing.FrozenSet[str]:
194
+ methods = urllib3.util.retry.Retry.DEFAULT_ALLOWED_METHODS
195
+ methods = methods.union({"PATCH"})
196
+ if retry_on_post:
197
+ methods = methods.union({"POST"})
198
+ return frozenset(methods)
199
+
165
200
  def _log_exception(self, level, exc, message, retry_count):
166
- getattr(logger, level)(
201
+ getattr(self._logger, level)(
167
202
  message,
168
203
  exception_type=type(exc),
169
204
  exception_message=err_to_str(exc),
mlrun/utils/logger.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2018 Iguazio
1
+ # Copyright 2023 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ import logging
17
17
  from enum import Enum
18
18
  from sys import stdout
19
19
  from traceback import format_exception
20
- from typing import IO, Union
20
+ from typing import IO, Optional, Union
21
21
 
22
22
  from mlrun.config import config
23
23
 
@@ -42,20 +42,39 @@ class JSONFormatter(logging.Formatter):
42
42
 
43
43
 
44
44
  class HumanReadableFormatter(logging.Formatter):
45
- def __init__(self):
46
- super(HumanReadableFormatter, self).__init__()
47
-
48
45
  def format(self, record):
46
+ record_with = self._record_with(record)
47
+ more = f": {record_with}" if record_with else ""
48
+ return f"> {self.formatTime(record, self.datefmt)} [{record.levelname.lower()}] {record.getMessage()}{more}"
49
+
50
+ def _record_with(self, record):
49
51
  record_with = getattr(record, "with", {})
50
52
  if record.exc_info:
51
53
  record_with.update(exc_info=format_exception(*record.exc_info))
54
+ return record_with
55
+
56
+
57
+ class HumanReadableExtendedFormatter(HumanReadableFormatter):
58
+ def format(self, record):
59
+ record_with = self._record_with(record)
52
60
  more = f": {record_with}" if record_with else ""
53
- return f"> {self.formatTime(record, self.datefmt)} [{record.levelname.lower()}] {record.getMessage()}{more}"
61
+ return (
62
+ "> "
63
+ f"{self.formatTime(record, self.datefmt)} "
64
+ f"[{record.name}:{record.levelname.lower()}] "
65
+ f"{record.getMessage()}{more}"
66
+ )
54
67
 
55
68
 
56
69
  class Logger(object):
57
- def __init__(self, level, name="mlrun", propagate=True):
58
- self._logger = logging.getLogger(name)
70
+ def __init__(
71
+ self,
72
+ level,
73
+ name="mlrun",
74
+ propagate=True,
75
+ logger: Optional[logging.Logger] = None,
76
+ ):
77
+ self._logger = logger or logging.getLogger(name)
59
78
  self._logger.propagate = propagate
60
79
  self._logger.setLevel(level)
61
80
  self._bound_variables = {}
@@ -90,6 +109,25 @@ class Logger(object):
90
109
  # add the handler to the logger
91
110
  self._logger.addHandler(stream_handler)
92
111
 
112
+ def get_child(self, suffix):
113
+ """
114
+ Get a child logger with the given suffix.
115
+ This is useful for when you want to have a logger for a specific component.
116
+ Once the formatter will support logger name, it will be easier to understand
117
+ which component logged the message.
118
+
119
+ :param suffix: The suffix to add to the logger name.
120
+ """
121
+ return Logger(
122
+ self.level,
123
+ # name is not set as it is provided by the "getChild"
124
+ name="",
125
+ # allowing child to delegate events logged to ancestor logger
126
+ # not doing so, will leave log lines not being handled
127
+ propagate=True,
128
+ logger=self._logger.getChild(suffix),
129
+ )
130
+
93
131
  @property
94
132
  def level(self):
95
133
  return self._logger.level
@@ -143,12 +181,14 @@ class Logger(object):
143
181
 
144
182
  class FormatterKinds(Enum):
145
183
  HUMAN = "human"
184
+ HUMAN_EXTENDED = "human_extended"
146
185
  JSON = "json"
147
186
 
148
187
 
149
188
  def _create_formatter_instance(formatter_kind: FormatterKinds) -> logging.Formatter:
150
189
  return {
151
190
  FormatterKinds.HUMAN: HumanReadableFormatter(),
191
+ FormatterKinds.HUMAN_EXTENDED: HumanReadableExtendedFormatter(),
152
192
  FormatterKinds.JSON: JSONFormatter(),
153
193
  }[formatter_kind]
154
194