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