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/projects/project.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.
@@ -14,7 +14,10 @@
14
14
  import datetime
15
15
  import getpass
16
16
  import glob
17
+ import http
18
+ import importlib.util as imputil
17
19
  import json
20
+ import os.path
18
21
  import pathlib
19
22
  import shutil
20
23
  import tempfile
@@ -31,21 +34,25 @@ import git.exc
31
34
  import inflection
32
35
  import kfp
33
36
  import nuclio
37
+ import requests
34
38
  import yaml
39
+ from deprecated import deprecated
35
40
 
36
- import mlrun.api.schemas
41
+ import mlrun.common.model_monitoring as model_monitoring_constants
42
+ import mlrun.common.schemas
37
43
  import mlrun.db
38
44
  import mlrun.errors
45
+ import mlrun.runtimes
46
+ import mlrun.runtimes.pod
47
+ import mlrun.runtimes.utils
39
48
  import mlrun.utils.regex
40
- from mlrun.runtimes import RuntimeKinds
41
49
 
42
50
  from ..artifacts import Artifact, ArtifactProducer, DatasetArtifact, ModelArtifact
43
51
  from ..artifacts.manager import ArtifactManager, dict_to_artifact, extend_artifact_path
44
52
  from ..datastore import store_manager
45
53
  from ..features import Feature
46
- from ..model import EntrypointParam, ModelObj
54
+ from ..model import EntrypointParam, ImageBuilder, ModelObj
47
55
  from ..run import code_to_function, get_object, import_function, new_function
48
- from ..runtimes.utils import add_code_metadata
49
56
  from ..secrets import SecretsStore
50
57
  from ..utils import (
51
58
  is_ipython,
@@ -57,7 +64,6 @@ from ..utils import (
57
64
  )
58
65
  from ..utils.clones import clone_git, clone_tgz, clone_zip, get_repo_url
59
66
  from ..utils.helpers import ensure_git_branch, resolve_git_reference_from_source
60
- from ..utils.model_monitoring import set_project_model_monitoring_credentials
61
67
  from ..utils.notifications import CustomNotificationPusher, NotificationTypes
62
68
  from .operations import (
63
69
  BuildStatus,
@@ -109,15 +115,20 @@ def new_project(
109
115
  subpath: str = None,
110
116
  save: bool = True,
111
117
  overwrite: bool = False,
118
+ parameters: dict = None,
112
119
  ) -> "MlrunProject":
113
120
  """Create a new MLRun project, optionally load it from a yaml/zip/git template
114
121
 
122
+ A new project is created and returned, you can customize the project by placing a project_setup.py file
123
+ in the project root dir, it will be executed upon project creation or loading.
124
+
125
+
115
126
  example::
116
127
 
117
- # create a project with local and marketplace functions, a workflow, and an artifact
128
+ # create a project with local and hub functions, a workflow, and an artifact
118
129
  project = mlrun.new_project("myproj", "./", init_git=True, description="my new project")
119
130
  project.set_function('prep_data.py', 'prep-data', image='mlrun/mlrun', handler='prep_data')
120
- project.set_function('hub://auto_trainer', 'train')
131
+ project.set_function('hub://auto-trainer', 'train')
121
132
  project.set_artifact('data', Artifact(target_path=data_url))
122
133
  project.set_workflow('main', "./myflow.py")
123
134
  project.save()
@@ -135,6 +146,16 @@ def new_project(
135
146
  project.run("main", watch=True)
136
147
 
137
148
 
149
+ example using project_setup.py to init the project objects::
150
+
151
+ def setup(project):
152
+ project.set_function('prep_data.py', 'prep-data', image='mlrun/mlrun', handler='prep_data')
153
+ project.set_function('hub://auto-trainer', 'train')
154
+ project.set_artifact('data', Artifact(target_path=data_url))
155
+ project.set_workflow('main', "./myflow.py")
156
+ return project
157
+
158
+
138
159
  :param name: project name
139
160
  :param context: project local directory path (default value = "./")
140
161
  :param init_git: if True, will git init the context dir
@@ -147,6 +168,7 @@ def new_project(
147
168
  :param save: whether to save the created project in the DB
148
169
  :param overwrite: overwrite project using 'cascade' deletion strategy (deletes project resources)
149
170
  if project with name exists
171
+ :param parameters: key/value pairs to add to the project.spec.params
150
172
 
151
173
  :returns: project object
152
174
  """
@@ -154,6 +176,10 @@ def new_project(
154
176
  name = _add_username_to_project_name_if_needed(name, user_project)
155
177
 
156
178
  if from_template:
179
+ if subpath:
180
+ raise mlrun.errors.MLRunInvalidArgumentError(
181
+ "Unsupported option, cannot use subpath argument with project templates"
182
+ )
157
183
  if from_template.endswith(".yaml"):
158
184
  project = _load_project_file(from_template, name, secrets)
159
185
  elif from_template.startswith("git://"):
@@ -169,7 +195,13 @@ def new_project(
169
195
  # Remove original owner name for avoiding possible conflicts
170
196
  project.spec.owner = None
171
197
  else:
172
- project = MlrunProject(name=name)
198
+ project = MlrunProject.from_dict(
199
+ {
200
+ "metadata": {
201
+ "name": name,
202
+ }
203
+ }
204
+ )
173
205
  project.spec.context = context
174
206
  project.spec.subpath = subpath or project.spec.subpath
175
207
 
@@ -182,14 +214,20 @@ def new_project(
182
214
  project.spec.origin_url = url
183
215
  if description:
184
216
  project.spec.description = description
217
+ if parameters:
218
+ # Enable setting project parameters at load time, can be used to customize the project_setup
219
+ for key, val in parameters.items():
220
+ project.spec.params[key] = val
185
221
 
186
222
  _set_as_current_default_project(project)
187
223
 
188
224
  if save and mlrun.mlconf.dbpath:
189
225
  if overwrite:
190
- logger.info(f"Deleting project {name} from MLRun DB due to overwrite")
226
+ logger.info(
227
+ "Overwriting project (by deleting and then creating)", name=name
228
+ )
191
229
  _delete_project_from_db(
192
- name, secrets, mlrun.api.schemas.DeletionStrategy.cascade
230
+ name, secrets, mlrun.common.schemas.DeletionStrategy.cascade
193
231
  )
194
232
 
195
233
  try:
@@ -200,12 +238,17 @@ def new_project(
200
238
  "Use overwrite=True to overwrite the existing project."
201
239
  ) from exc
202
240
  logger.info(
203
- f"Created and saved project {name}",
241
+ "Created and saved project",
242
+ name=name,
204
243
  from_template=from_template,
205
244
  overwrite=overwrite,
206
245
  context=context,
207
246
  save=save,
208
247
  )
248
+
249
+ # Hook for initializing the project using a project_setup script
250
+ project = project.setup(save and mlrun.mlconf.dbpath)
251
+
209
252
  return project
210
253
 
211
254
 
@@ -220,10 +263,15 @@ def load_project(
220
263
  user_project: bool = False,
221
264
  save: bool = True,
222
265
  sync_functions: bool = False,
266
+ parameters: dict = None,
223
267
  ) -> "MlrunProject":
224
268
  """Load an MLRun project from git or tar or dir
225
269
 
226
- example::
270
+ MLRun looks for a project.yaml file with project definition and objects in the project root path
271
+ and use it to initialize the project, in addition it runs the project_setup.py file (if it exists)
272
+ for further customization.
273
+
274
+ Usage example::
227
275
 
228
276
  # Load the project and run the 'main' workflow.
229
277
  # When using git as the url source the context directory must be an empty or
@@ -231,6 +279,21 @@ def load_project(
231
279
  project = load_project("./demo_proj", "git://github.com/mlrun/project-demo.git")
232
280
  project.run("main", arguments={'data': data_url})
233
281
 
282
+
283
+ project_setup.py example::
284
+
285
+ def setup(project):
286
+ train_function = project.set_function(
287
+ "src/trainer.py",
288
+ name="mpi-training",
289
+ kind="mpijob",
290
+ image="mlrun/ml-models",
291
+ )
292
+ # Set the number of replicas for the training from the project parameter
293
+ train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
294
+ return project
295
+
296
+
234
297
  :param context: project local directory path (default value = "./")
235
298
  :param url: name (in DB) or git or tar.gz or .zip sources archive path e.g.:
236
299
  git://github.com/mlrun/demo-xgb-project.git
@@ -246,6 +309,7 @@ def load_project(
246
309
  :param user_project: add the current user name to the project name (for db:// prefixes)
247
310
  :param save: whether to save the created project and artifact in the DB
248
311
  :param sync_functions: sync the project's functions into the project object (will be saved to the DB if save=True)
312
+ :param parameters: key/value pairs to add to the project.spec.params
249
313
 
250
314
  :returns: project object
251
315
  """
@@ -271,13 +335,18 @@ def load_project(
271
335
  clone_tgz(url, context, secrets, clone)
272
336
  elif url.endswith(".zip"):
273
337
  clone_zip(url, context, secrets, clone)
274
- else:
338
+ elif url.startswith("db://") or "://" not in url:
275
339
  project = _load_project_from_db(url, secrets, user_project)
276
340
  project.spec.context = context
277
341
  if not path.isdir(context):
278
342
  makedirs(context)
279
343
  project.spec.subpath = subpath or project.spec.subpath
280
344
  from_db = True
345
+ else:
346
+ raise mlrun.errors.MLRunInvalidArgumentError(
347
+ "Unsupported url scheme, supported schemes are: git://, db:// or "
348
+ ".zip/.tar.gz/.yaml file path (could be local or remote) or project name which will be loaded from DB"
349
+ )
281
350
 
282
351
  if not repo:
283
352
  repo, url = init_repo(context, url, init_git)
@@ -287,6 +356,12 @@ def load_project(
287
356
 
288
357
  if not project.metadata.name:
289
358
  raise ValueError("project name must be specified")
359
+
360
+ if parameters:
361
+ # Enable setting project parameters at load time, can be used to customize the project_setup
362
+ for key, val in parameters.items():
363
+ project.spec.params[key] = val
364
+
290
365
  if not from_db:
291
366
  project.spec.source = url or project.spec.source
292
367
  project.spec.origin_url = url or project.spec.origin_url
@@ -301,14 +376,18 @@ def load_project(
301
376
  except Exception:
302
377
  pass
303
378
 
304
- if save and mlrun.mlconf.dbpath:
379
+ to_save = save and mlrun.mlconf.dbpath
380
+ if to_save:
305
381
  project.save()
382
+
383
+ # Hook for initializing the project using a project_setup script
384
+ project = project.setup(to_save)
385
+
386
+ if to_save:
306
387
  project.register_artifacts()
307
- if sync_functions:
308
- project.sync_functions(names=project.get_function_names(), save=True)
309
388
 
310
- elif sync_functions:
311
- project.sync_functions(names=project.get_function_names(), save=False)
389
+ if sync_functions:
390
+ project.sync_functions(save=to_save)
312
391
 
313
392
  _set_as_current_default_project(project)
314
393
 
@@ -326,31 +405,55 @@ def get_or_create_project(
326
405
  user_project: bool = False,
327
406
  from_template: str = None,
328
407
  save: bool = True,
408
+ parameters: dict = None,
329
409
  ) -> "MlrunProject":
330
410
  """Load a project from MLRun DB, or create/import if doesnt exist
331
411
 
332
- example::
412
+ MLRun looks for a project.yaml file with project definition and objects in the project root path
413
+ and use it to initialize the project, in addition it runs the project_setup.py file (if it exists)
414
+ for further customization.
415
+
416
+ Usage example::
333
417
 
334
418
  # load project from the DB (if exist) or the source repo
335
419
  project = get_or_create_project("myproj", "./", "git://github.com/mlrun/demo-xgb-project.git")
336
420
  project.pull("development") # pull the latest code from git
337
421
  project.run("main", arguments={'data': data_url}) # run the workflow "main"
338
422
 
423
+
424
+ project_setup.py example::
425
+
426
+ def setup(project):
427
+ train_function = project.set_function(
428
+ "src/trainer.py",
429
+ name="mpi-training",
430
+ kind="mpijob",
431
+ image="mlrun/ml-models",
432
+ )
433
+ # Set the number of replicas for the training from the project parameter
434
+ train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
435
+ return project
436
+
437
+
339
438
  :param name: project name
340
439
  :param context: project local directory path (default value = "./")
341
440
  :param url: name (in DB) or git or tar.gz or .zip sources archive path e.g.:
342
441
  git://github.com/mlrun/demo-xgb-project.git
343
442
  http://mysite/archived-project.zip
344
443
  :param secrets: key:secret dict or SecretsStore used to download sources
345
- :param init_git: if True, will excute `git init` on the context dir
444
+ :param init_git: if True, will execute `git init` on the context dir
346
445
  :param subpath: project subpath (within the archive/context)
347
446
  :param clone: if True, always clone (delete any existing content)
348
447
  :param user_project: add the current username to the project name (for db:// prefixes)
349
448
  :param from_template: path to project YAML file that will be used as from_template (for new projects)
350
449
  :param save: whether to save the created project in the DB
450
+ :param parameters: key/value pairs to add to the project.spec.params
451
+
351
452
  :returns: project object
352
453
  """
353
454
  context = context or "./"
455
+ spec_path = path.join(context, subpath or "", "project.yaml")
456
+ load_from_path = url or path.isfile(spec_path)
354
457
  try:
355
458
  # load project from the DB.
356
459
  # use `name` as `url` as we load the project from the DB
@@ -365,51 +468,107 @@ def get_or_create_project(
365
468
  user_project=user_project,
366
469
  # only loading project from db so no need to save it
367
470
  save=False,
471
+ parameters=parameters,
368
472
  )
369
- logger.info(f"loaded project {name} from MLRun DB")
473
+ logger.info("Project loaded successfully", project_name=name)
370
474
  return project
371
475
 
372
476
  except mlrun.errors.MLRunNotFoundError:
373
- spec_path = path.join(context, subpath or "", "project.yaml")
374
- if url or path.isfile(spec_path):
375
- # load project from archive or local project.yaml
376
- project = load_project(
377
- context,
378
- url,
379
- name,
380
- secrets=secrets,
381
- init_git=init_git,
382
- subpath=subpath,
383
- clone=clone,
384
- user_project=user_project,
385
- save=save,
386
- )
387
- message = f"loaded project {name} from {url or context}"
388
- if save:
389
- message = f"{message} and saved in MLRun DB"
390
- logger.info(message)
391
- else:
392
- # create a new project
393
- project = new_project(
394
- name,
395
- context,
396
- init_git=init_git,
397
- user_project=user_project,
398
- from_template=from_template,
399
- secrets=secrets,
400
- subpath=subpath,
401
- save=save,
477
+ logger.debug("Project not found in db", project_name=name)
478
+
479
+ # do not nest under "try" or else the exceptions raised below will be logged along with the "not found" message
480
+ if load_from_path:
481
+ # loads a project from archive or local project.yaml
482
+ logger.info("Loading project from path", project_name=name, path=url or context)
483
+ project = load_project(
484
+ context,
485
+ url,
486
+ name,
487
+ secrets=secrets,
488
+ init_git=init_git,
489
+ subpath=subpath,
490
+ clone=clone,
491
+ user_project=user_project,
492
+ save=save,
493
+ parameters=parameters,
494
+ )
495
+
496
+ logger.info(
497
+ "Project loaded successfully",
498
+ project_name=name,
499
+ path=url or context,
500
+ stored_in_db=save,
501
+ )
502
+ return project
503
+
504
+ # create a new project
505
+ project = new_project(
506
+ name,
507
+ context,
508
+ init_git=init_git,
509
+ user_project=user_project,
510
+ from_template=from_template,
511
+ secrets=secrets,
512
+ subpath=subpath,
513
+ save=save,
514
+ parameters=parameters,
515
+ )
516
+ logger.info("Project created successfully", project_name=name, stored_in_db=save)
517
+ return project
518
+
519
+
520
+ def _run_project_setup(
521
+ project: "MlrunProject", setup_file_path: str, save: bool = False
522
+ ):
523
+ """Run the project setup file if found
524
+
525
+ When loading a project MLRun will look for a project_setup.py file, if it is found
526
+ it will execute the setup(project) handler, which can enrich the project with additional
527
+ objects, functions, artifacts, etc.
528
+
529
+ Example::
530
+
531
+ def setup(project):
532
+ train_function = project.set_function(
533
+ "src/trainer.py",
534
+ name="mpi-training",
535
+ kind="mpijob",
536
+ image="mlrun/ml-models",
402
537
  )
403
- message = f"created project {name}"
404
- if save:
405
- message = f"{message} and saved in MLRun DB"
406
- logger.info(message)
538
+ # Set the number of replicas for the training from the project parameter
539
+ train_function.spec.replicas = project.spec.params.get("num_replicas", 1)
540
+ return project
541
+
542
+ """
543
+ if not path.exists(setup_file_path):
407
544
  return project
545
+ spec = imputil.spec_from_file_location("workflow", setup_file_path)
546
+ if spec is None:
547
+ raise ImportError(f"cannot import project setup file in {setup_file_path}")
548
+ mod = imputil.module_from_spec(spec)
549
+ spec.loader.exec_module(mod)
550
+
551
+ if hasattr(mod, "setup"):
552
+ try:
553
+ project = getattr(mod, "setup")(project)
554
+ except Exception as exc:
555
+ logger.error(
556
+ "Failed to run project_setup script",
557
+ setup_file_path=setup_file_path,
558
+ exc=mlrun.errors.err_to_str(exc),
559
+ )
560
+ raise exc
561
+ if save:
562
+ project.save()
563
+ else:
564
+ logger.warn("skipping setup, setup() handler was not found in project_setup.py")
565
+ return project
408
566
 
409
567
 
410
568
  def _load_project_dir(context, name="", subpath=""):
411
569
  subpath_str = subpath or ""
412
570
  fpath = path.join(context, subpath_str, "project.yaml")
571
+ setup_file_path = path.join(context, subpath_str, "project_setup.py")
413
572
  if path.isfile(fpath):
414
573
  with open(fpath) as fp:
415
574
  data = fp.read()
@@ -419,10 +578,19 @@ def _load_project_dir(context, name="", subpath=""):
419
578
 
420
579
  elif path.isfile(path.join(context, subpath_str, "function.yaml")):
421
580
  func = import_function(path.join(context, subpath_str, "function.yaml"))
422
- project = MlrunProject(
423
- name=func.metadata.project,
424
- functions=[{"url": "function.yaml", "name": func.metadata.name}],
581
+ project = MlrunProject.from_dict(
582
+ {
583
+ "metadata": {
584
+ "name": func.metadata.project,
585
+ },
586
+ "spec": {
587
+ "functions": [{"url": "function.yaml", "name": func.metadata.name}],
588
+ },
589
+ }
425
590
  )
591
+ elif path.exists(setup_file_path):
592
+ # If there is a setup script do not force having project.yaml file
593
+ project = MlrunProject()
426
594
  else:
427
595
  raise mlrun.errors.MLRunNotFoundError(
428
596
  "project or function YAML not found in path"
@@ -526,11 +694,13 @@ class ProjectSpec(ModelObj):
526
694
  goals=None,
527
695
  load_source_on_run=None,
528
696
  default_requirements: typing.Union[str, typing.List[str]] = None,
529
- desired_state=mlrun.api.schemas.ProjectState.online.value,
697
+ desired_state=mlrun.common.schemas.ProjectState.online.value,
530
698
  owner=None,
531
699
  disable_auto_mount=None,
532
700
  workdir=None,
533
701
  default_image=None,
702
+ build=None,
703
+ custom_packagers: typing.List[typing.Tuple[str, bool]] = None,
534
704
  ):
535
705
  self.repo = None
536
706
 
@@ -564,6 +734,13 @@ class ProjectSpec(ModelObj):
564
734
  self.disable_auto_mount = disable_auto_mount
565
735
  self.default_image = default_image
566
736
 
737
+ self.build = build
738
+
739
+ # A list of custom packagers to include when running the functions of the project. A custom packager is stored
740
+ # in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
741
+ # whether it is mandatory for a run (raise exception on collection error) or not.
742
+ self.custom_packagers = custom_packagers or []
743
+
567
744
  @property
568
745
  def source(self) -> str:
569
746
  """source url or git repo"""
@@ -573,8 +750,6 @@ class ProjectSpec(ModelObj):
573
750
  if url:
574
751
  self._source = url
575
752
 
576
- if self._source in [".", "./"]:
577
- return path.abspath(self.context)
578
753
  return self._source
579
754
 
580
755
  @source.setter
@@ -730,6 +905,54 @@ class ProjectSpec(ModelObj):
730
905
  if key in self._artifacts:
731
906
  del self._artifacts[key]
732
907
 
908
+ @property
909
+ def build(self) -> ImageBuilder:
910
+ return self._build
911
+
912
+ @build.setter
913
+ def build(self, build):
914
+ self._build = self._verify_dict(build, "build", ImageBuilder)
915
+
916
+ def add_custom_packager(self, packager: str, is_mandatory: bool):
917
+ """
918
+ Add a custom packager from the custom packagers list.
919
+
920
+ :param packager: The packager module path to add. For example, if a packager `MyPackager` is in the
921
+ project's source at my_module.py, then the module path is: "my_module.MyPackager".
922
+ :param is_mandatory: Whether this packager must be collected during a run. If False, failing to collect it won't
923
+ raise an error during the packagers collection phase.
924
+ """
925
+ # TODO: enable importing packagers from the hub.
926
+ if packager in [
927
+ custom_packager[0] for custom_packager in self.custom_packagers
928
+ ]:
929
+ logger.warn(
930
+ f"The packager's module path '{packager}' is already registered in the project."
931
+ )
932
+ return
933
+ self.custom_packagers.append((packager, is_mandatory))
934
+
935
+ def remove_custom_packager(self, packager: str):
936
+ """
937
+ Remove a custom packager from the custom packagers list.
938
+
939
+ :param packager: The packager module path to remove.
940
+
941
+ :raise MLRunInvalidArgumentError: In case the packager was not in the list.
942
+ """
943
+ # Look for the packager tuple in the list to remove it:
944
+ packager_tuple: typing.Tuple[str, bool] = None
945
+ for custom_packager in self.custom_packagers:
946
+ if custom_packager[0] == packager:
947
+ packager_tuple = custom_packager
948
+
949
+ # If not found, raise an error, otherwise remove:
950
+ if packager_tuple is None:
951
+ raise mlrun.errors.MLRunInvalidArgumentError(
952
+ f"The packager module path '{packager}' is not registered in the project, hence it cannot be removed."
953
+ )
954
+ self.custom_packagers.remove(packager_tuple)
955
+
733
956
  def _source_repo(self):
734
957
  src = self.source
735
958
  if src:
@@ -769,6 +992,7 @@ class MlrunProject(ModelObj):
769
992
 
770
993
  def __init__(
771
994
  self,
995
+ # TODO: remove all arguments except metadata and spec in 1.6.0
772
996
  name=None,
773
997
  description=None,
774
998
  params=None,
@@ -777,7 +1001,7 @@ class MlrunProject(ModelObj):
777
1001
  artifacts=None,
778
1002
  artifact_path=None,
779
1003
  conda=None,
780
- # all except these 2 are for backwards compatibility with MlrunProjectLegacy
1004
+ # all except these metadata and spec are for backwards compatibility with MlrunProjectLegacy
781
1005
  metadata=None,
782
1006
  spec=None,
783
1007
  default_requirements: typing.Union[str, typing.List[str]] = None,
@@ -789,6 +1013,26 @@ class MlrunProject(ModelObj):
789
1013
  self._status = None
790
1014
  self.status = None
791
1015
 
1016
+ if any(
1017
+ [
1018
+ name,
1019
+ description,
1020
+ params,
1021
+ functions,
1022
+ workflows,
1023
+ artifacts,
1024
+ artifact_path,
1025
+ conda,
1026
+ default_requirements,
1027
+ ]
1028
+ ):
1029
+ # TODO: remove in 1.6.0 along with all arguments except metadata and spec
1030
+ warnings.warn(
1031
+ "Project constructor arguments are deprecated in 1.4.0 and will be removed in 1.6.0,"
1032
+ " use metadata and spec instead",
1033
+ FutureWarning,
1034
+ )
1035
+
792
1036
  # Handling the fields given in the legacy way
793
1037
  self.metadata.name = name or self.metadata.name
794
1038
  self.spec.description = description or self.spec.description
@@ -866,20 +1110,28 @@ class MlrunProject(ModelObj):
866
1110
  def source(self, source):
867
1111
  self.spec.source = source
868
1112
 
869
- def set_source(self, source, pull_at_runtime=False, workdir=None):
1113
+ def set_source(
1114
+ self,
1115
+ source: str = "",
1116
+ pull_at_runtime: bool = False,
1117
+ workdir: Optional[str] = None,
1118
+ ):
870
1119
  """set the project source code path(can be git/tar/zip archive)
871
1120
 
872
- :param source: valid path to git, zip, or tar file, (or None for current) e.g.
873
- git://github.com/mlrun/something.git
874
- http://some/url/file.zip
1121
+ :param source: valid absolute path or URL to git, zip, or tar file, (or None for current) e.g.
1122
+ git://github.com/mlrun/something.git
1123
+ http://some/url/file.zip
1124
+ note path source must exist on the image or exist locally when run is local
1125
+ (it is recommended to use 'workdir' when source is a filepath instead)
875
1126
  :param pull_at_runtime: load the archive into the container at job runtime vs on build/deploy
876
- :param workdir: the relative workdir path (under the context dir)
1127
+ :param workdir: workdir path relative to the context dir or absolute
877
1128
  """
1129
+ mlrun.utils.helpers.validate_builder_source(source, pull_at_runtime, workdir)
1130
+
878
1131
  self.spec.load_source_on_run = pull_at_runtime
879
1132
  self.spec.source = source or self.spec.source
880
1133
 
881
1134
  if self.spec.source.startswith("git://"):
882
-
883
1135
  source, reference, branch = resolve_git_reference_from_source(source)
884
1136
  if not branch and not reference:
885
1137
  logger.warn(
@@ -892,20 +1144,23 @@ class MlrunProject(ModelObj):
892
1144
  self.sync_functions()
893
1145
 
894
1146
  def get_artifact_uri(
895
- self, key: str, category: str = "artifact", tag: str = None
1147
+ self, key: str, category: str = "artifact", tag: str = None, iter: int = None
896
1148
  ) -> str:
897
1149
  """return the project artifact uri (store://..) from the artifact key
898
1150
 
899
1151
  example::
900
1152
 
901
- uri = project.get_artifact_uri("my_model", category="model", tag="prod")
1153
+ uri = project.get_artifact_uri("my_model", category="model", tag="prod", iter=0)
902
1154
 
903
1155
  :param key: artifact key/name
904
1156
  :param category: artifact category (artifact, model, feature-vector, ..)
905
1157
  :param tag: artifact version tag, default to latest version
1158
+ :param iter: iteration number, default to no iteration
906
1159
  """
907
1160
  uri = f"store://{category}s/{self.metadata.name}/{key}"
908
- if tag:
1161
+ if iter is not None:
1162
+ uri = f"{uri}#{iter}"
1163
+ if tag is not None:
909
1164
  uri = f"{uri}:{tag}"
910
1165
  return uri
911
1166
 
@@ -989,7 +1244,7 @@ class MlrunProject(ModelObj):
989
1244
  engine=None,
990
1245
  args_schema: typing.List[EntrypointParam] = None,
991
1246
  handler=None,
992
- schedule: typing.Union[str, mlrun.api.schemas.ScheduleCronTrigger] = None,
1247
+ schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
993
1248
  ttl=None,
994
1249
  **args,
995
1250
  ):
@@ -1008,11 +1263,24 @@ class MlrunProject(ModelObj):
1008
1263
  :param ttl: pipeline ttl in secs (after that the pods will be removed)
1009
1264
  :param args: argument values (key=value, ..)
1010
1265
  """
1011
- if not workflow_path:
1012
- raise ValueError("valid workflow_path must be specified")
1266
+
1267
+ # validate the provided workflow_path
1268
+ if mlrun.utils.helpers.is_file_path_invalid(
1269
+ self.spec.get_code_path(), workflow_path
1270
+ ):
1271
+ raise ValueError(
1272
+ f"Invalid 'workflow_path': '{workflow_path}'. Please provide a valid URL/path to a file."
1273
+ )
1274
+
1013
1275
  if embed:
1014
- if self.spec.context and not workflow_path.startswith("/"):
1015
- workflow_path = path.join(self.spec.context, workflow_path)
1276
+ if (
1277
+ self.context
1278
+ and not workflow_path.startswith("/")
1279
+ # since the user may provide a path the includes the context,
1280
+ # we need to make sure we don't add it twice
1281
+ and not workflow_path.startswith(self.context)
1282
+ ):
1283
+ workflow_path = path.join(self.context, workflow_path)
1016
1284
  with open(workflow_path, "r") as fp:
1017
1285
  txt = fp.read()
1018
1286
  workflow = {"name": name, "code": txt}
@@ -1087,11 +1355,13 @@ class MlrunProject(ModelObj):
1087
1355
  artifact_path = mlrun.utils.helpers.fill_artifact_path_template(
1088
1356
  self.spec.artifact_path or mlrun.mlconf.artifact_path, self.metadata.name
1089
1357
  )
1358
+ # TODO: To correctly maintain the list of artifacts from an exported project,
1359
+ # we need to maintain the different trees that generated them
1090
1360
  producer = ArtifactProducer(
1091
1361
  "project",
1092
1362
  self.metadata.name,
1093
1363
  self.metadata.name,
1094
- tag=self._get_hexsha() or "latest",
1364
+ tag=self._get_hexsha() or str(uuid.uuid4()),
1095
1365
  )
1096
1366
  for artifact_dict in self.spec.artifacts:
1097
1367
  if _is_imported_artifact(artifact_dict):
@@ -1436,12 +1706,15 @@ class MlrunProject(ModelObj):
1436
1706
  with open(f"{temp_dir}/_body", "rb") as fp:
1437
1707
  artifact.spec._body = fp.read()
1438
1708
  artifact.target_path = ""
1709
+
1710
+ # if the dataitem is not a file, it means we downloaded it from a remote source to a temp file,
1711
+ # so we need to remove it after we're done with it
1712
+ dataitem.remove_local()
1713
+
1439
1714
  return self.log_artifact(
1440
1715
  artifact, local_path=temp_dir, artifact_path=artifact_path
1441
1716
  )
1442
1717
 
1443
- if dataitem.kind != "file":
1444
- remove(item_file)
1445
1718
  else:
1446
1719
  raise ValueError("unsupported file suffix, use .yaml, .json, or .zip")
1447
1720
 
@@ -1471,6 +1744,21 @@ class MlrunProject(ModelObj):
1471
1744
  self.__dict__.update(project.__dict__)
1472
1745
  return project
1473
1746
 
1747
+ def setup(self, save: bool = True) -> "MlrunProject":
1748
+ """Run the project setup file if found
1749
+
1750
+ When loading a project MLRun will look for a project_setup.py file, if it is found
1751
+ it will execute the setup(project) handler, which can enrich the project with additional
1752
+ objects, functions, artifacts, etc.
1753
+
1754
+ :param save: save the project after the setup
1755
+ """
1756
+ # Hook for initializing the project using a project_setup script
1757
+ setup_file_path = path.join(
1758
+ self.context, self.spec.subpath or "", "project_setup.py"
1759
+ )
1760
+ return _run_project_setup(self, setup_file_path, save)
1761
+
1474
1762
  def set_function(
1475
1763
  self,
1476
1764
  func: typing.Union[str, mlrun.runtimes.BaseRuntime] = None,
@@ -1481,6 +1769,7 @@ class MlrunProject(ModelObj):
1481
1769
  with_repo: bool = None,
1482
1770
  tag: str = None,
1483
1771
  requirements: typing.Union[str, typing.List[str]] = None,
1772
+ requirements_file: str = "",
1484
1773
  ) -> mlrun.runtimes.BaseRuntime:
1485
1774
  """update or add a function object to the project
1486
1775
 
@@ -1489,7 +1778,7 @@ class MlrunProject(ModelObj):
1489
1778
 
1490
1779
  object (s3://, v3io://, ..)
1491
1780
  MLRun DB e.g. db://project/func:ver
1492
- functions hub/market: e.g. hub://auto_trainer:master
1781
+ functions hub/market: e.g. hub://auto-trainer:master
1493
1782
 
1494
1783
  examples::
1495
1784
 
@@ -1508,16 +1797,20 @@ class MlrunProject(ModelObj):
1508
1797
  # by providing a path to a pip requirements file
1509
1798
  proj.set_function('my.py', requirements="requirements.txt")
1510
1799
 
1511
- :param func: function object or spec/code url, None refers to current Notebook
1512
- :param name: name of the function (under the project)
1513
- :param kind: runtime kind e.g. job, nuclio, spark, dask, mpijob
1514
- default: job
1515
- :param image: docker image to be used, can also be specified in
1516
- the function object/yaml
1517
- :param handler: default function handler to invoke (can only be set with .py/.ipynb files)
1518
- :param with_repo: add (clone) the current repo to the build source
1519
- :param tag: function version tag (none for 'latest', can only be set with .py/.ipynb files)
1520
- :param requirements: list of python packages or pip requirements file path
1800
+ :param func: function object or spec/code url, None refers to current Notebook
1801
+ :param name: name of the function (under the project), can be specified with a tag to support
1802
+ versions (e.g. myfunc:v1)
1803
+ :param kind: runtime kind e.g. job, nuclio, spark, dask, mpijob
1804
+ default: job
1805
+ :param image: docker image to be used, can also be specified in
1806
+ the function object/yaml
1807
+ :param handler: default function handler to invoke (can only be set with .py/.ipynb files)
1808
+ :param with_repo: add (clone) the current repo to the build source
1809
+ :param tag: function version tag (none for 'latest', can only be set with .py/.ipynb files)
1810
+ if tag is specified and name is empty, the function key (under the project)
1811
+ will be enriched with the tag value. (i.e. 'function-name:tag')
1812
+ :param requirements: a list of python packages
1813
+ :param requirements_file: path to a python requirements file
1521
1814
 
1522
1815
  :returns: project object
1523
1816
  """
@@ -1536,6 +1829,7 @@ class MlrunProject(ModelObj):
1536
1829
  func = path.relpath(func, self.spec.context)
1537
1830
 
1538
1831
  func = func or ""
1832
+ name = mlrun.utils.normalize_name(name) if name else name
1539
1833
  if isinstance(func, str):
1540
1834
  # in hub or db functions name defaults to the function name
1541
1835
  if not name and not (func.startswith("db://") or func.startswith("hub://")):
@@ -1551,10 +1845,14 @@ class MlrunProject(ModelObj):
1551
1845
  "requirements": requirements,
1552
1846
  }
1553
1847
  func = {k: v for k, v in function_dict.items() if v}
1554
- name, function_object = _init_function_from_dict(func, self)
1555
- func["name"] = name
1848
+ resolved_function_name, function_object = _init_function_from_dict(
1849
+ func, self
1850
+ )
1851
+ func["name"] = resolved_function_name
1556
1852
  elif hasattr(func, "to_dict"):
1557
- name, function_object = _init_function_from_obj(func, self, name=name)
1853
+ resolved_function_name, function_object = _init_function_from_obj(
1854
+ func, self, name=name
1855
+ )
1558
1856
  if handler:
1559
1857
  raise ValueError(
1560
1858
  "default handler cannot be set for existing function object"
@@ -1562,15 +1860,23 @@ class MlrunProject(ModelObj):
1562
1860
  if image:
1563
1861
  function_object.spec.image = image
1564
1862
  if with_repo:
1863
+ # mark source to be enriched before run with project source (enrich_function_object)
1565
1864
  function_object.spec.build.source = "./"
1566
1865
  if requirements:
1567
- function_object.with_requirements(requirements)
1568
- if not name:
1866
+ function_object.with_requirements(
1867
+ requirements, requirements_file=requirements_file
1868
+ )
1869
+ if not resolved_function_name:
1569
1870
  raise ValueError("function name must be specified")
1570
1871
  else:
1571
1872
  raise ValueError("func must be a function url or object")
1572
1873
 
1573
- self.spec.set_function(name, function_object, func)
1874
+ # if function name was not explicitly provided,
1875
+ # we use the resolved name (from the function object) and add the tag
1876
+ if tag and not name and ":" not in resolved_function_name:
1877
+ resolved_function_name = f"{resolved_function_name}:{tag}"
1878
+
1879
+ self.spec.set_function(resolved_function_name, function_object, func)
1574
1880
  return function_object
1575
1881
 
1576
1882
  def remove_function(self, name):
@@ -1587,33 +1893,65 @@ class MlrunProject(ModelObj):
1587
1893
  enrich=False,
1588
1894
  ignore_cache=False,
1589
1895
  copy_function=True,
1896
+ tag: str = "",
1590
1897
  ) -> mlrun.runtimes.BaseRuntime:
1591
1898
  """get function object by name
1592
1899
 
1593
- :param key: name of key for search
1594
- :param sync: will reload/reinit the function from the project spec
1595
- :param enrich: add project info/config/source info to the function object
1596
- :param ignore_cache: read the function object from the DB (ignore the local cache)
1597
- :param copy_function: return a copy of the function object
1900
+ :param key: name of key for search
1901
+ :param sync: will reload/reinit the function from the project spec
1902
+ :param enrich: add project info/config/source info to the function object
1903
+ :param ignore_cache: read the function object from the DB (ignore the local cache)
1904
+ :param copy_function: return a copy of the function object
1905
+ :param tag: provide if the function key is tagged under the project (function was set with a tag)
1598
1906
 
1599
1907
  :returns: function object
1600
1908
  """
1601
- if key in self.spec._function_objects and not sync and not ignore_cache:
1602
- function = self.spec._function_objects[key]
1603
- elif key in self.spec._function_definitions and not ignore_cache:
1604
- self.sync_functions([key])
1605
- function = self.spec._function_objects[key]
1606
- else:
1607
- function = get_db_function(self, key)
1608
- self.spec._function_objects[key] = function
1909
+ if tag and ":" not in key:
1910
+ key = f"{key}:{tag}"
1911
+
1912
+ function, err = self._get_function(
1913
+ mlrun.utils.normalize_name(key), sync, ignore_cache
1914
+ )
1915
+ if not function and "_" in key:
1916
+ function, err = self._get_function(key, sync, ignore_cache)
1917
+
1918
+ if not function:
1919
+ raise err
1920
+
1609
1921
  if enrich:
1610
1922
  function = enrich_function_object(
1611
1923
  self, function, copy_function=copy_function
1612
1924
  )
1613
1925
  self.spec._function_objects[key] = function
1926
+
1614
1927
  return function
1615
1928
 
1616
- def get_function_objects(self) -> typing.Dict[str, mlrun.runtimes.BaseRuntime]:
1929
+ def _get_function(self, key, sync, ignore_cache):
1930
+ """
1931
+ Function can be retrieved from the project spec (cache) or from the database.
1932
+ In sync mode, we first perform a sync of the function_objects from the function_definitions,
1933
+ and then returning it from the function_objects (if exists).
1934
+ When not in sync mode, we verify and return from the function objects directly.
1935
+ In ignore_cache mode, we query the function from the database rather than from the project spec.
1936
+ """
1937
+ if key in self.spec._function_objects and not sync and not ignore_cache:
1938
+ function = self.spec._function_objects[key]
1939
+
1940
+ elif key in self.spec._function_definitions and not ignore_cache:
1941
+ self.sync_functions([key])
1942
+ function = self.spec._function_objects[key]
1943
+ else:
1944
+ try:
1945
+ function = get_db_function(self, key)
1946
+ self.spec._function_objects[key] = function
1947
+ except requests.HTTPError as exc:
1948
+ if exc.response.status_code != http.HTTPStatus.NOT_FOUND.value:
1949
+ raise exc
1950
+ return None, exc
1951
+
1952
+ return function, None
1953
+
1954
+ def get_function_objects(self) -> FunctionsDict:
1617
1955
  """ "get a virtual dict with all the project functions ready for use in a pipeline"""
1618
1956
  self.sync_functions()
1619
1957
  return FunctionsDict(self)
@@ -1712,7 +2050,7 @@ class MlrunProject(ModelObj):
1712
2050
  if not names:
1713
2051
  names = self.spec._function_definitions.keys()
1714
2052
  funcs = {}
1715
- origin = add_code_metadata(self.spec.context)
2053
+ origin = mlrun.runtimes.utils.add_code_metadata(self.spec.context)
1716
2054
  for name in names:
1717
2055
  f = self.spec._function_definitions.get(name)
1718
2056
  if not f:
@@ -1779,7 +2117,7 @@ class MlrunProject(ModelObj):
1779
2117
  self,
1780
2118
  secrets: dict = None,
1781
2119
  file_path: str = None,
1782
- provider: typing.Union[str, mlrun.api.schemas.SecretProviderName] = None,
2120
+ provider: typing.Union[str, mlrun.common.schemas.SecretProviderName] = None,
1783
2121
  ):
1784
2122
  """set project secrets from dict or secrets env file
1785
2123
  when using a secrets file it should have lines in the form KEY=VALUE, comment line start with "#"
@@ -1806,18 +2144,21 @@ class MlrunProject(ModelObj):
1806
2144
  "must specify secrets OR file_path"
1807
2145
  )
1808
2146
  if file_path:
1809
- secrets = dotenv.dotenv_values(file_path)
1810
- if None in secrets.values():
1811
- raise mlrun.errors.MLRunInvalidArgumentError(
1812
- "env file lines must be in the form key=value"
1813
- )
2147
+ if path.isfile(file_path):
2148
+ secrets = dotenv.dotenv_values(file_path)
2149
+ if None in secrets.values():
2150
+ raise mlrun.errors.MLRunInvalidArgumentError(
2151
+ "env file lines must be in the form key=value"
2152
+ )
2153
+ else:
2154
+ raise mlrun.errors.MLRunNotFoundError(f"{file_path} does not exist")
1814
2155
  # drop V3IO paths/credentials and MLrun service API address
1815
2156
  env_vars = {
1816
2157
  key: val
1817
2158
  for key, val in secrets.items()
1818
2159
  if key != "MLRUN_DBPATH" and not key.startswith("V3IO_")
1819
2160
  }
1820
- provider = provider or mlrun.api.schemas.SecretProviderName.kubernetes
2161
+ provider = provider or mlrun.common.schemas.SecretProviderName.kubernetes
1821
2162
  mlrun.db.get_run_db().create_project_secrets(
1822
2163
  self.metadata.name, provider=provider, secrets=env_vars
1823
2164
  )
@@ -1862,7 +2203,9 @@ class MlrunProject(ModelObj):
1862
2203
  ttl: int = None,
1863
2204
  engine: str = None,
1864
2205
  local: bool = None,
1865
- schedule: typing.Union[str, mlrun.api.schemas.ScheduleCronTrigger, bool] = None,
2206
+ schedule: typing.Union[
2207
+ str, mlrun.common.schemas.ScheduleCronTrigger, bool
2208
+ ] = None,
1866
2209
  timeout: int = None,
1867
2210
  overwrite: bool = False,
1868
2211
  source: str = None,
@@ -1938,7 +2281,10 @@ class MlrunProject(ModelObj):
1938
2281
 
1939
2282
  self.sync_functions(always=sync)
1940
2283
  if not self.spec._function_objects:
1941
- raise ValueError("no functions in the project")
2284
+ raise ValueError(
2285
+ "There are no functions in the project."
2286
+ " Make sure you've set your functions with project.set_function()."
2287
+ )
1942
2288
 
1943
2289
  if not name and not workflow_path and not workflow_handler:
1944
2290
  if self.spec.workflows:
@@ -1951,9 +2297,9 @@ class MlrunProject(ModelObj):
1951
2297
  else:
1952
2298
  workflow_spec = self.spec._workflows[name].copy()
1953
2299
  workflow_spec.merge_args(arguments)
1954
- workflow_spec.cleanup_ttl = (
1955
- cleanup_ttl or ttl or workflow_spec.cleanup_ttl or workflow_spec.ttl
1956
- )
2300
+ workflow_spec.cleanup_ttl = (
2301
+ cleanup_ttl or ttl or workflow_spec.cleanup_ttl or workflow_spec.ttl
2302
+ )
1957
2303
  workflow_spec.run_local = local
1958
2304
 
1959
2305
  name = f"{self.metadata.name}-{name}" if name else self.metadata.name
@@ -2039,14 +2385,43 @@ class MlrunProject(ModelObj):
2039
2385
  notifiers=notifiers,
2040
2386
  )
2041
2387
 
2388
+ # TODO: remove in 1.6.0
2389
+ @deprecated(
2390
+ version="1.4.0",
2391
+ reason="'clear_context' will be removed in 1.6.0, this can cause unexpected issues",
2392
+ category=FutureWarning,
2393
+ )
2042
2394
  def clear_context(self):
2043
2395
  """delete all files and clear the context dir"""
2044
- if (
2045
- self.spec.context
2046
- and path.exists(self.spec.context)
2047
- and path.isdir(self.spec.context)
2048
- ):
2049
- shutil.rmtree(self.spec.context)
2396
+ warnings.warn(
2397
+ "This method deletes all files and clears the context directory or subpath (if defined)!"
2398
+ " Please keep in mind that this method can produce unexpected outcomes and is not recommended,"
2399
+ " it will be deprecated in 1.6.0."
2400
+ )
2401
+ # clear only if the context path exists and not relative
2402
+ if self.spec.context and os.path.isabs(self.spec.context):
2403
+
2404
+ # if a subpath is defined, will empty the subdir instead of the entire context
2405
+ if self.spec.subpath:
2406
+ path_to_clear = path.join(self.spec.context, self.spec.subpath)
2407
+ logger.info(f"Subpath is defined, Clearing path: {path_to_clear}")
2408
+ else:
2409
+ path_to_clear = self.spec.context
2410
+ logger.info(
2411
+ f"Subpath is not defined, Clearing context: {path_to_clear}"
2412
+ )
2413
+ if path.exists(path_to_clear) and path.isdir(path_to_clear):
2414
+ shutil.rmtree(path_to_clear)
2415
+ else:
2416
+ logger.warn(
2417
+ f"Attempt to clear {path_to_clear} failed. Path either does not exist or is not a directory."
2418
+ " Please ensure that your context or subdpath are properly defined."
2419
+ )
2420
+ else:
2421
+ logger.warn(
2422
+ "Your context path is a relative path;"
2423
+ " in order to avoid unexpected results, we do not allow the deletion of relative paths."
2424
+ )
2050
2425
 
2051
2426
  def save(self, filepath=None, store=True):
2052
2427
  """export project to yaml file and save project in database
@@ -2105,15 +2480,43 @@ class MlrunProject(ModelObj):
2105
2480
  mlrun.get_dataitem(filepath).upload(tmp_path)
2106
2481
  remove(tmp_path)
2107
2482
 
2108
- def set_model_monitoring_credentials(self, access_key: str):
2483
+ def set_model_monitoring_credentials(
2484
+ self,
2485
+ access_key: str = None,
2486
+ endpoint_store_connection: str = None,
2487
+ stream_path: str = None,
2488
+ ):
2109
2489
  """Set the credentials that will be used by the project's model monitoring
2110
2490
  infrastructure functions.
2111
- The supplied credentials must have data access
2112
2491
 
2113
- :param access_key: Model Monitoring access key for managing user permissions.
2492
+ :param access_key: Model Monitoring access key for managing user permissions
2493
+ :param endpoint_store_connection: Endpoint store connection string
2494
+ :param stream_path: Path to the model monitoring stream
2114
2495
  """
2115
- set_project_model_monitoring_credentials(
2116
- access_key=access_key, project=self.metadata.name
2496
+
2497
+ secrets_dict = {}
2498
+ if access_key:
2499
+ secrets_dict[
2500
+ model_monitoring_constants.ProjectSecretKeys.ACCESS_KEY
2501
+ ] = access_key
2502
+
2503
+ if endpoint_store_connection:
2504
+ secrets_dict[
2505
+ model_monitoring_constants.ProjectSecretKeys.ENDPOINT_STORE_CONNECTION
2506
+ ] = endpoint_store_connection
2507
+
2508
+ if stream_path:
2509
+ if stream_path.startswith("kafka://") and "?topic" in stream_path:
2510
+ raise mlrun.errors.MLRunInvalidArgumentError(
2511
+ "Custom kafka topic is not allowed"
2512
+ )
2513
+ secrets_dict[
2514
+ model_monitoring_constants.ProjectSecretKeys.STREAM_PATH
2515
+ ] = stream_path
2516
+
2517
+ self.set_secrets(
2518
+ secrets=secrets_dict,
2519
+ provider=mlrun.common.schemas.SecretProviderName.kubernetes,
2117
2520
  )
2118
2521
 
2119
2522
  def run_function(
@@ -2134,18 +2537,19 @@ class MlrunProject(ModelObj):
2134
2537
  verbose: bool = None,
2135
2538
  selector: str = None,
2136
2539
  auto_build: bool = None,
2137
- schedule: typing.Union[str, mlrun.api.schemas.ScheduleCronTrigger] = None,
2540
+ schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
2138
2541
  artifact_path: str = None,
2542
+ notifications: typing.List[mlrun.model.Notification] = None,
2139
2543
  returns: Optional[List[Union[str, Dict[str, str]]]] = None,
2140
2544
  ) -> typing.Union[mlrun.model.RunObject, kfp.dsl.ContainerOp]:
2141
2545
  """Run a local or remote task as part of a local/kubeflow pipeline
2142
2546
 
2143
2547
  example (use with project)::
2144
2548
 
2145
- # create a project with two functions (local and from marketplace)
2549
+ # create a project with two functions (local and from hub)
2146
2550
  project = mlrun.new_project(project_name, "./proj")
2147
2551
  project.set_function("mycode.py", "myfunc", image="mlrun/mlrun")
2148
- project.set_function("hub://auto_trainer", "train")
2552
+ project.set_function("hub://auto-trainer", "train")
2149
2553
 
2150
2554
  # run functions (refer to them by name)
2151
2555
  run1 = project.run_function("myfunc", params={"x": 7})
@@ -2177,6 +2581,7 @@ class MlrunProject(ModelObj):
2177
2581
  see this link for help:
2178
2582
  https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#module-apscheduler.triggers.cron
2179
2583
  :param artifact_path: path to store artifacts, when running in a workflow this will be set automatically
2584
+ :param notifications: list of notifications to push when the run is completed
2180
2585
  :param returns: List of log hints - configurations for how to log the returning values from the
2181
2586
  handler's run (as artifacts or results). The list's length must be equal to the amount
2182
2587
  of returning objects. A log hint may be given as:
@@ -2211,6 +2616,7 @@ class MlrunProject(ModelObj):
2211
2616
  auto_build=auto_build,
2212
2617
  schedule=schedule,
2213
2618
  artifact_path=artifact_path,
2619
+ notifications=notifications,
2214
2620
  returns=returns,
2215
2621
  )
2216
2622
 
@@ -2219,28 +2625,30 @@ class MlrunProject(ModelObj):
2219
2625
  function: typing.Union[str, mlrun.runtimes.BaseRuntime],
2220
2626
  with_mlrun: bool = None,
2221
2627
  skip_deployed: bool = False,
2222
- image=None,
2223
- base_image=None,
2628
+ image: str = None,
2629
+ base_image: str = None,
2224
2630
  commands: list = None,
2225
- secret_name=None,
2631
+ secret_name: str = None,
2226
2632
  requirements: typing.Union[str, typing.List[str]] = None,
2227
- mlrun_version_specifier=None,
2633
+ mlrun_version_specifier: str = None,
2228
2634
  builder_env: dict = None,
2229
2635
  overwrite_build_params: bool = False,
2636
+ requirements_file: str = None,
2230
2637
  ) -> typing.Union[BuildStatus, kfp.dsl.ContainerOp]:
2231
2638
  """deploy ML function, build container with its dependencies
2232
2639
 
2233
- :param function: name of the function (in the project) or function object
2234
- :param with_mlrun: add the current mlrun package to the container build
2235
- :param skip_deployed: skip the build if we already have an image for the function
2236
- :param image: target image name/path
2237
- :param base_image: base image name/path (commands and source code will be added to it)
2238
- :param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
2239
- :param secret_name: k8s secret for accessing the docker registry
2240
- :param requirements: list of python packages or pip requirements file path, defaults to None
2640
+ :param function: name of the function (in the project) or function object
2641
+ :param with_mlrun: add the current mlrun package to the container build
2642
+ :param skip_deployed: skip the build if we already have an image for the function
2643
+ :param image: target image name/path
2644
+ :param base_image: base image name/path (commands and source code will be added to it)
2645
+ :param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
2646
+ :param secret_name: k8s secret for accessing the docker registry
2647
+ :param requirements: list of python packages, defaults to None
2648
+ :param requirements_file: pip requirements file path, defaults to None
2241
2649
  :param mlrun_version_specifier: which mlrun package version to include (if not current)
2242
- :param builder_env: Kaniko builder pod env vars dict (for config/credentials)
2243
- e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
2650
+ :param builder_env: Kaniko builder pod env vars dict (for config/credentials)
2651
+ e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
2244
2652
  :param overwrite_build_params: overwrite the function build parameters with the provided ones, or attempt to
2245
2653
  add to existing parameters
2246
2654
  """
@@ -2253,12 +2661,141 @@ class MlrunProject(ModelObj):
2253
2661
  commands=commands,
2254
2662
  secret_name=secret_name,
2255
2663
  requirements=requirements,
2664
+ requirements_file=requirements_file,
2256
2665
  mlrun_version_specifier=mlrun_version_specifier,
2257
2666
  builder_env=builder_env,
2258
2667
  project_object=self,
2259
2668
  overwrite_build_params=overwrite_build_params,
2260
2669
  )
2261
2670
 
2671
+ def build_config(
2672
+ self,
2673
+ image: str = None,
2674
+ set_as_default: bool = False,
2675
+ with_mlrun: bool = None,
2676
+ base_image: str = None,
2677
+ commands: list = None,
2678
+ secret_name: str = None,
2679
+ requirements: typing.Union[str, typing.List[str]] = None,
2680
+ overwrite_build_params: bool = False,
2681
+ requirements_file: str = None,
2682
+ ):
2683
+ """specify builder configuration for the project
2684
+
2685
+ :param image: target image name/path. If not specified the project's existing `default_image` name will be
2686
+ used. If not set, the `mlconf.default_project_image_name` value will be used
2687
+ :param set_as_default: set `image` to be the project's default image (default False)
2688
+ :param with_mlrun: add the current mlrun package to the container build
2689
+ :param base_image: base image name/path
2690
+ :param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
2691
+ :param secret_name: k8s secret for accessing the docker registry
2692
+ :param requirements: a list of packages to install on the built image
2693
+ :param requirements_file: requirements file to install on the built image
2694
+ :param overwrite_build_params: overwrite existing build configuration (default False)
2695
+
2696
+ * False: the new params are merged with the existing (currently merge is applied to requirements and
2697
+ commands)
2698
+ * True: the existing params are replaced by the new ones
2699
+ """
2700
+ default_image_name = mlrun.mlconf.default_project_image_name.format(
2701
+ name=self.name
2702
+ )
2703
+ image = image or self.default_image or default_image_name
2704
+
2705
+ self.spec.build.build_config(
2706
+ image=image,
2707
+ base_image=base_image,
2708
+ commands=commands,
2709
+ secret=secret_name,
2710
+ with_mlrun=with_mlrun,
2711
+ requirements=requirements,
2712
+ requirements_file=requirements_file,
2713
+ overwrite=overwrite_build_params,
2714
+ )
2715
+
2716
+ if set_as_default and image != self.default_image:
2717
+ self.set_default_image(image)
2718
+
2719
+ def build_image(
2720
+ self,
2721
+ image: str = None,
2722
+ set_as_default: bool = True,
2723
+ with_mlrun: bool = None,
2724
+ skip_deployed: bool = False,
2725
+ base_image: str = None,
2726
+ commands: list = None,
2727
+ secret_name: str = None,
2728
+ requirements: typing.Union[str, typing.List[str]] = None,
2729
+ mlrun_version_specifier: str = None,
2730
+ builder_env: dict = None,
2731
+ overwrite_build_params: bool = False,
2732
+ requirements_file: str = None,
2733
+ ) -> typing.Union[BuildStatus, kfp.dsl.ContainerOp]:
2734
+ """Builder docker image for the project, based on the project's build config. Parameters allow to override
2735
+ the build config.
2736
+
2737
+ :param image: target image name/path. If not specified the project's existing `default_image` name will be
2738
+ used. If not set, the `mlconf.default_project_image_name` value will be used
2739
+ :param set_as_default: set `image` to be the project's default image (default False)
2740
+ :param with_mlrun: add the current mlrun package to the container build
2741
+ :param skip_deployed: skip the build if we already have the image specified built
2742
+ :param base_image: base image name/path (commands and source code will be added to it)
2743
+ :param commands: list of docker build (RUN) commands e.g. ['pip install pandas']
2744
+ :param secret_name: k8s secret for accessing the docker registry
2745
+ :param requirements: list of python packages, defaults to None
2746
+ :param requirements_file: pip requirements file path, defaults to None
2747
+ :param mlrun_version_specifier: which mlrun package version to include (if not current)
2748
+ :param builder_env: Kaniko builder pod env vars dict (for config/credentials)
2749
+ e.g. builder_env={"GIT_TOKEN": token}, does not work yet in KFP
2750
+ :param overwrite_build_params: overwrite existing build configuration (default False)
2751
+
2752
+ * False: the new params are merged with the existing (currently merge is applied to requirements and
2753
+ commands)
2754
+ * True: the existing params are replaced by the new ones
2755
+ """
2756
+
2757
+ self.build_config(
2758
+ image=image,
2759
+ set_as_default=set_as_default,
2760
+ base_image=base_image,
2761
+ commands=commands,
2762
+ secret_name=secret_name,
2763
+ with_mlrun=with_mlrun,
2764
+ requirements=requirements,
2765
+ requirements_file=requirements_file,
2766
+ overwrite_build_params=overwrite_build_params,
2767
+ )
2768
+
2769
+ function = mlrun.new_function("mlrun--project--image--builder", kind="job")
2770
+
2771
+ build = self.spec.build
2772
+ result = self.build_function(
2773
+ function=function,
2774
+ with_mlrun=build.with_mlrun,
2775
+ image=build.image,
2776
+ base_image=build.base_image,
2777
+ commands=build.commands,
2778
+ secret_name=build.secret,
2779
+ requirements=build.requirements,
2780
+ skip_deployed=skip_deployed,
2781
+ overwrite_build_params=overwrite_build_params,
2782
+ mlrun_version_specifier=mlrun_version_specifier,
2783
+ builder_env=builder_env,
2784
+ )
2785
+
2786
+ try:
2787
+ mlrun.db.get_run_db(secrets=self._secrets).delete_function(
2788
+ name=function.metadata.name
2789
+ )
2790
+ except Exception as exc:
2791
+ logger.warning(
2792
+ f"Image was successfully built, but failed to delete temporary function {function.metadata.name}."
2793
+ " To remove the function, attempt to manually delete it.",
2794
+ exc=repr(exc),
2795
+ )
2796
+
2797
+ return result
2798
+
2262
2799
  def deploy_function(
2263
2800
  self,
2264
2801
  function: typing.Union[str, mlrun.runtimes.BaseRuntime],
@@ -2315,7 +2852,7 @@ class MlrunProject(ModelObj):
2315
2852
  iter: int = None,
2316
2853
  best_iteration: bool = False,
2317
2854
  kind: str = None,
2318
- category: typing.Union[str, mlrun.api.schemas.ArtifactCategories] = None,
2855
+ category: typing.Union[str, mlrun.common.schemas.ArtifactCategories] = None,
2319
2856
  ) -> mlrun.lists.ArtifactList:
2320
2857
  """List artifacts filtered by various parameters.
2321
2858
 
@@ -2329,8 +2866,9 @@ class MlrunProject(ModelObj):
2329
2866
  # check different artifact versions for a specific artifact, return as objects list
2330
2867
  result_versions = project.list_artifacts('results', tag='*').to_objects()
2331
2868
 
2332
- :param name: Name of artifacts to retrieve. Name is used as a like query, and is not case-sensitive. This means
2333
- that querying for ``name`` may return artifacts named ``my_Name_1`` or ``surname``.
2869
+ :param name: Name of artifacts to retrieve. Name with '~' prefix is used as a like query, and is not
2870
+ case-sensitive. This means that querying for ``~name`` may return artifacts named
2871
+ ``my_Name_1`` or ``surname``.
2334
2872
  :param tag: Return artifacts assigned this tag.
2335
2873
  :param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
2336
2874
  a list of "label=value" (match label key and value) or "label" (match just label key) strings.
@@ -2376,8 +2914,9 @@ class MlrunProject(ModelObj):
2376
2914
  latest_models = project.list_models('', tag='latest')
2377
2915
 
2378
2916
 
2379
- :param name: Name of artifacts to retrieve. Name is used as a like query, and is not case-sensitive. This means
2380
- that querying for ``name`` may return artifacts named ``my_Name_1`` or ``surname``.
2917
+ :param name: Name of artifacts to retrieve. Name with '~' prefix is used as a like query, and is not
2918
+ case-sensitive. This means that querying for ``~name`` may return artifacts named
2919
+ ``my_Name_1`` or ``surname``.
2381
2920
  :param tag: Return artifacts assigned this tag.
2382
2921
  :param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
2383
2922
  a list of "label=value" (match label key and value) or "label" (match just label key) strings.
@@ -2423,17 +2962,17 @@ class MlrunProject(ModelObj):
2423
2962
 
2424
2963
  def list_runs(
2425
2964
  self,
2426
- name=None,
2427
- uid=None,
2428
- labels=None,
2429
- state=None,
2430
- sort=True,
2431
- last=0,
2432
- iter=False,
2433
- start_time_from: datetime.datetime = None,
2434
- start_time_to: datetime.datetime = None,
2435
- last_update_time_from: datetime.datetime = None,
2436
- last_update_time_to: datetime.datetime = None,
2965
+ name: Optional[str] = None,
2966
+ uid: Optional[Union[str, List[str]]] = None,
2967
+ labels: Optional[Union[str, List[str]]] = None,
2968
+ state: Optional[str] = None,
2969
+ sort: bool = True,
2970
+ last: int = 0,
2971
+ iter: bool = False,
2972
+ start_time_from: Optional[datetime.datetime] = None,
2973
+ start_time_to: Optional[datetime.datetime] = None,
2974
+ last_update_time_from: Optional[datetime.datetime] = None,
2975
+ last_update_time_to: Optional[datetime.datetime] = None,
2437
2976
  **kwargs,
2438
2977
  ) -> mlrun.lists.RunList:
2439
2978
  """Retrieve a list of runs, filtered by various options.
@@ -2447,6 +2986,10 @@ class MlrunProject(ModelObj):
2447
2986
  # return a list of runs matching the name and label and compare
2448
2987
  runs = project.list_runs(name='download', labels='owner=admin')
2449
2988
  runs.compare()
2989
+
2990
+ # multi-label filter can also be provided
2991
+ runs = project.list_runs(name='download', labels=["kind=job", "owner=admin"])
2992
+
2450
2993
  # If running in Jupyter, can use the .show() function to display the results
2451
2994
  project.list_runs(name='').show()
2452
2995
 
@@ -2454,8 +2997,8 @@ class MlrunProject(ModelObj):
2454
2997
  :param name: Name of the run to retrieve.
2455
2998
  :param uid: Unique ID of the run.
2456
2999
  :param project: Project that the runs belongs to.
2457
- :param labels: List runs that have a specific label assigned. Currently only a single label filter can be
2458
- applied, otherwise result will be empty.
3000
+ :param labels: List runs that have specific labels assigned. a single or multi label filter can be
3001
+ applied.
2459
3002
  :param state: List only runs whose state is specified.
2460
3003
  :param sort: Whether to sort the result according to their start time. Otherwise, results will be
2461
3004
  returned by their internal order in the DB (order will not be guaranteed).
@@ -2484,13 +3027,53 @@ class MlrunProject(ModelObj):
2484
3027
  **kwargs,
2485
3028
  )
2486
3029
 
3030
+ def get_custom_packagers(self) -> typing.List[typing.Tuple[str, bool]]:
3031
+ """
3032
+ Get the custom packagers registered in the project.
3033
+
3034
+ :return: A list of the custom packagers module paths.
3035
+ """
3036
+ # Return a copy so the user won't be able to edit the list by the reference returned (no need for deep copy as
3037
+ # tuples do not support item assignment):
3038
+ return self.spec.custom_packagers.copy()
3039
+
3040
+ def add_custom_packager(self, packager: str, is_mandatory: bool):
3041
+ """
3042
+ Add a custom packager from the custom packagers list. All project's custom packagers are added to each project
3043
+ function.
3044
+
3045
+ **Notice** that in order to run a function with the custom packagers included, you must set a source for the
3046
+ project (using the `project.set_source` method) with the parameter `pull_at_runtime=True` so the source code of
3047
+ the packagers will be able to be imported.
3048
+
3049
+ :param packager: The packager module path to add. For example, if a packager `MyPackager` is in the
3050
+ project's source at my_module.py, then the module path is: "my_module.MyPackager".
3051
+ :param is_mandatory: Whether this packager must be collected during a run. If False, failing to collect it won't
3052
+ raise an error during the packagers collection phase.
3053
+ """
3054
+ self.spec.add_custom_packager(packager=packager, is_mandatory=is_mandatory)
3055
+
3056
+ def remove_custom_packager(self, packager: str):
3057
+ """
3058
+ Remove a custom packager from the custom packagers list.
3059
+
3060
+ :param packager: The packager module path to remove.
3061
+
3062
+ :raise MLRunInvalidArgumentError: In case the packager was not in the list.
3063
+ """
3064
+ self.spec.remove_custom_packager(packager=packager)
3065
+
2487
3066
 
2488
3067
  def _set_as_current_default_project(project: MlrunProject):
2489
3068
  mlrun.mlconf.default_project = project.metadata.name
2490
3069
  pipeline_context.set(project)
2491
3070
 
2492
3071
 
2493
- def _init_function_from_dict(f, project, name=None):
3072
+ def _init_function_from_dict(
3073
+ f: dict,
3074
+ project: MlrunProject,
3075
+ name: typing.Optional[str] = None,
3076
+ ) -> typing.Tuple[str, mlrun.runtimes.BaseRuntime]:
2494
3077
  name = name or f.get("name", "")
2495
3078
  url = f.get("url", "")
2496
3079
  kind = f.get("kind", "")
@@ -2556,6 +3139,7 @@ def _init_function_from_dict(f, project, name=None):
2556
3139
  raise ValueError(f"unsupported function url:handler {url}:{handler} or no spec")
2557
3140
 
2558
3141
  if with_repo:
3142
+ # mark source to be enriched before run with project source (enrich_function_object)
2559
3143
  func.spec.build.source = "./"
2560
3144
  if requirements:
2561
3145
  func.with_requirements(requirements)
@@ -2563,7 +3147,11 @@ def _init_function_from_dict(f, project, name=None):
2563
3147
  return _init_function_from_obj(func, project, name)
2564
3148
 
2565
3149
 
2566
- def _init_function_from_obj(func, project, name=None):
3150
+ def _init_function_from_obj(
3151
+ func: mlrun.runtimes.BaseRuntime,
3152
+ project: MlrunProject,
3153
+ name: typing.Optional[str] = None,
3154
+ ) -> typing.Tuple[str, mlrun.runtimes.BaseRuntime]:
2567
3155
  build = func.spec.build
2568
3156
  if project.spec.origin_url:
2569
3157
  origin = project.spec.origin_url
@@ -2580,76 +3168,12 @@ def _init_function_from_obj(func, project, name=None):
2580
3168
  return name or func.metadata.name, func
2581
3169
 
2582
3170
 
2583
- def _init_function_from_dict_legacy(f, project):
2584
- name = f.get("name", "")
2585
- url = f.get("url", "")
2586
- kind = f.get("kind", "")
2587
- image = f.get("image", None)
2588
- with_repo = f.get("with_repo", False)
2589
-
2590
- if with_repo and not project.source:
2591
- raise ValueError("project source must be specified when cloning context")
2592
-
2593
- in_context = False
2594
- if not url and "spec" not in f:
2595
- raise ValueError("function missing a url or a spec")
2596
- # We are not using the project method to obtain an absolute path here,
2597
- # because legacy projects are built differently, and we cannot rely on them to have a spec
2598
- if url and "://" not in url:
2599
- if project.context and not url.startswith("/"):
2600
- url = path.join(project.context, url)
2601
- in_context = True
2602
- if not path.isfile(url):
2603
- raise OSError(f"{url} not found")
2604
-
2605
- if "spec" in f:
2606
- func = new_function(name, runtime=f["spec"])
2607
- elif is_yaml_path(url) or url.startswith("db://") or url.startswith("hub://"):
2608
- func = import_function(url)
2609
- if image:
2610
- func.spec.image = image
2611
- elif url.endswith(".ipynb"):
2612
- func = code_to_function(name, filename=url, image=image, kind=kind)
2613
- elif url.endswith(".py"):
2614
- if not image:
2615
- raise ValueError(
2616
- "image must be provided with py code files, "
2617
- "use function object for more control/settings"
2618
- )
2619
- if in_context and with_repo:
2620
- func = new_function(name, command=url, image=image, kind=kind or "job")
2621
- else:
2622
- func = code_to_function(name, filename=url, image=image, kind=kind or "job")
2623
- else:
2624
- raise ValueError(f"unsupported function url {url} or no spec")
2625
-
2626
- if with_repo:
2627
- func.spec.build.source = "./"
2628
-
2629
- return _init_function_from_obj_legacy(func, project, name)
2630
-
2631
-
2632
- def _init_function_from_obj_legacy(func, project, name=None):
2633
- build = func.spec.build
2634
- if project.origin_url:
2635
- origin = project.origin_url
2636
- try:
2637
- if project.repo:
2638
- origin += "#" + project.repo.head.commit.hexsha
2639
- except Exception:
2640
- pass
2641
- build.code_origin = origin
2642
- if project.name:
2643
- func.metadata.project = project.name
2644
- if project.tag:
2645
- func.metadata.tag = project.tag
2646
- return name or func.metadata.name, func
2647
-
2648
-
2649
3171
  def _has_module(handler, kind):
2650
3172
  if not handler:
2651
3173
  return False
2652
- return (kind in RuntimeKinds.nuclio_runtimes() and ":" in handler) or "." in handler
3174
+ return (
3175
+ kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes() and ":" in handler
3176
+ ) or "." in handler
2653
3177
 
2654
3178
 
2655
3179
  def _is_imported_artifact(artifact):