mlrun 1.6.0rc35__py3-none-any.whl → 1.7.0rc2__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/__main__.py +3 -3
- mlrun/api/schemas/__init__.py +1 -1
- mlrun/artifacts/base.py +11 -6
- mlrun/artifacts/dataset.py +2 -2
- mlrun/artifacts/model.py +30 -24
- mlrun/artifacts/plots.py +2 -2
- mlrun/common/db/sql_session.py +5 -3
- mlrun/common/helpers.py +1 -2
- mlrun/common/schemas/artifact.py +3 -3
- mlrun/common/schemas/auth.py +3 -3
- mlrun/common/schemas/background_task.py +1 -1
- mlrun/common/schemas/client_spec.py +1 -1
- mlrun/common/schemas/feature_store.py +16 -16
- mlrun/common/schemas/frontend_spec.py +7 -7
- mlrun/common/schemas/function.py +1 -1
- mlrun/common/schemas/hub.py +4 -9
- mlrun/common/schemas/memory_reports.py +2 -2
- mlrun/common/schemas/model_monitoring/grafana.py +4 -4
- mlrun/common/schemas/model_monitoring/model_endpoints.py +14 -15
- mlrun/common/schemas/notification.py +4 -4
- mlrun/common/schemas/object.py +2 -2
- mlrun/common/schemas/pipeline.py +1 -1
- mlrun/common/schemas/project.py +3 -3
- mlrun/common/schemas/runtime_resource.py +8 -12
- mlrun/common/schemas/schedule.py +3 -3
- mlrun/common/schemas/tag.py +1 -2
- mlrun/common/schemas/workflow.py +2 -2
- mlrun/config.py +8 -4
- mlrun/data_types/to_pandas.py +1 -3
- mlrun/datastore/base.py +0 -28
- mlrun/datastore/datastore_profile.py +9 -9
- mlrun/datastore/filestore.py +0 -1
- mlrun/datastore/google_cloud_storage.py +1 -1
- mlrun/datastore/sources.py +7 -11
- mlrun/datastore/spark_utils.py +1 -2
- mlrun/datastore/targets.py +31 -31
- mlrun/datastore/utils.py +4 -6
- mlrun/datastore/v3io.py +70 -46
- mlrun/db/base.py +22 -23
- mlrun/db/httpdb.py +34 -34
- mlrun/db/nopdb.py +19 -19
- mlrun/errors.py +1 -1
- mlrun/execution.py +4 -4
- mlrun/feature_store/api.py +20 -21
- mlrun/feature_store/common.py +1 -1
- mlrun/feature_store/feature_set.py +28 -32
- mlrun/feature_store/feature_vector.py +24 -27
- mlrun/feature_store/retrieval/base.py +7 -7
- mlrun/feature_store/retrieval/conversion.py +2 -4
- mlrun/feature_store/steps.py +7 -15
- mlrun/features.py +5 -7
- mlrun/frameworks/_common/artifacts_library.py +9 -9
- mlrun/frameworks/_common/mlrun_interface.py +5 -5
- mlrun/frameworks/_common/model_handler.py +48 -48
- mlrun/frameworks/_common/plan.py +2 -3
- mlrun/frameworks/_common/producer.py +3 -4
- mlrun/frameworks/_common/utils.py +5 -5
- mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +16 -35
- mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
- mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
- mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
- mlrun/frameworks/_ml_common/model_handler.py +24 -24
- mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/_ml_common/utils.py +4 -4
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +7 -7
- mlrun/frameworks/huggingface/model_server.py +4 -4
- mlrun/frameworks/lgbm/__init__.py +32 -32
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
- mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
- mlrun/frameworks/lgbm/model_handler.py +9 -9
- mlrun/frameworks/lgbm/model_server.py +6 -6
- mlrun/frameworks/lgbm/utils.py +5 -5
- mlrun/frameworks/onnx/dataset.py +8 -8
- mlrun/frameworks/onnx/mlrun_interface.py +3 -3
- mlrun/frameworks/onnx/model_handler.py +6 -6
- mlrun/frameworks/onnx/model_server.py +7 -7
- mlrun/frameworks/parallel_coordinates.py +2 -2
- mlrun/frameworks/pytorch/__init__.py +16 -16
- mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
- mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
- mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
- mlrun/frameworks/pytorch/model_handler.py +17 -17
- mlrun/frameworks/pytorch/model_server.py +7 -7
- mlrun/frameworks/sklearn/__init__.py +12 -12
- mlrun/frameworks/sklearn/estimator.py +4 -4
- mlrun/frameworks/sklearn/metrics_library.py +14 -14
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
- mlrun/frameworks/sklearn/model_handler.py +2 -2
- mlrun/frameworks/tf_keras/__init__.py +5 -5
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +14 -14
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
- mlrun/frameworks/tf_keras/mlrun_interface.py +7 -9
- mlrun/frameworks/tf_keras/model_handler.py +14 -14
- mlrun/frameworks/tf_keras/model_server.py +6 -6
- mlrun/frameworks/xgboost/__init__.py +12 -12
- mlrun/frameworks/xgboost/model_handler.py +6 -6
- mlrun/k8s_utils.py +4 -5
- mlrun/kfpops.py +2 -2
- mlrun/launcher/base.py +10 -10
- mlrun/launcher/local.py +8 -8
- mlrun/launcher/remote.py +7 -7
- mlrun/lists.py +3 -4
- mlrun/model.py +205 -55
- mlrun/model_monitoring/api.py +21 -24
- mlrun/model_monitoring/application.py +4 -4
- mlrun/model_monitoring/batch.py +17 -17
- mlrun/model_monitoring/controller.py +2 -1
- mlrun/model_monitoring/features_drift_table.py +44 -31
- mlrun/model_monitoring/prometheus.py +1 -4
- mlrun/model_monitoring/stores/kv_model_endpoint_store.py +11 -13
- mlrun/model_monitoring/stores/model_endpoint_store.py +9 -11
- mlrun/model_monitoring/stores/models/__init__.py +2 -2
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +11 -13
- mlrun/model_monitoring/stream_processing.py +16 -34
- mlrun/model_monitoring/tracking_policy.py +2 -1
- mlrun/package/__init__.py +6 -6
- mlrun/package/context_handler.py +5 -5
- mlrun/package/packager.py +7 -7
- mlrun/package/packagers/default_packager.py +6 -6
- mlrun/package/packagers/numpy_packagers.py +15 -15
- mlrun/package/packagers/pandas_packagers.py +5 -5
- mlrun/package/packagers/python_standard_library_packagers.py +10 -10
- mlrun/package/packagers_manager.py +18 -23
- mlrun/package/utils/_formatter.py +4 -4
- mlrun/package/utils/_pickler.py +2 -2
- mlrun/package/utils/_supported_format.py +4 -4
- mlrun/package/utils/log_hint_utils.py +2 -2
- mlrun/package/utils/type_hint_utils.py +4 -9
- mlrun/platforms/other.py +1 -2
- mlrun/projects/operations.py +5 -5
- mlrun/projects/pipelines.py +9 -9
- mlrun/projects/project.py +58 -46
- mlrun/render.py +1 -1
- mlrun/run.py +9 -9
- mlrun/runtimes/__init__.py +7 -4
- mlrun/runtimes/base.py +20 -23
- mlrun/runtimes/constants.py +5 -5
- mlrun/runtimes/daskjob.py +8 -8
- mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
- mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
- mlrun/runtimes/function_reference.py +1 -1
- mlrun/runtimes/local.py +1 -1
- mlrun/runtimes/mpijob/abstract.py +1 -2
- mlrun/runtimes/nuclio/__init__.py +20 -0
- mlrun/runtimes/{function.py → nuclio/function.py} +15 -16
- mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
- mlrun/runtimes/{serving.py → nuclio/serving.py} +13 -12
- mlrun/runtimes/pod.py +95 -48
- mlrun/runtimes/remotesparkjob.py +1 -1
- mlrun/runtimes/sparkjob/spark3job.py +50 -33
- mlrun/runtimes/utils.py +1 -2
- mlrun/secrets.py +3 -3
- mlrun/serving/remote.py +0 -4
- mlrun/serving/routers.py +6 -6
- mlrun/serving/server.py +4 -4
- mlrun/serving/states.py +29 -0
- mlrun/serving/utils.py +3 -3
- mlrun/serving/v1_serving.py +6 -7
- mlrun/serving/v2_serving.py +50 -8
- mlrun/track/tracker_manager.py +3 -3
- mlrun/track/trackers/mlflow_tracker.py +1 -2
- mlrun/utils/async_http.py +5 -7
- mlrun/utils/azure_vault.py +1 -1
- mlrun/utils/clones.py +1 -2
- mlrun/utils/condition_evaluator.py +3 -3
- mlrun/utils/db.py +3 -3
- mlrun/utils/helpers.py +37 -119
- mlrun/utils/http.py +1 -4
- mlrun/utils/logger.py +49 -14
- mlrun/utils/notifications/notification/__init__.py +3 -3
- mlrun/utils/notifications/notification/base.py +2 -2
- mlrun/utils/notifications/notification/ipython.py +1 -1
- mlrun/utils/notifications/notification_pusher.py +8 -14
- mlrun/utils/retryer.py +207 -0
- mlrun/utils/singleton.py +1 -1
- mlrun/utils/v3io_clients.py +2 -3
- mlrun/utils/version/version.json +2 -2
- mlrun/utils/version/version.py +2 -6
- {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/METADATA +9 -9
- mlrun-1.7.0rc2.dist-info/RECORD +315 -0
- mlrun-1.6.0rc35.dist-info/RECORD +0 -313
- {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/LICENSE +0 -0
- {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/WHEEL +0 -0
- {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/entry_points.txt +0 -0
- {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/top_level.txt +0 -0
mlrun/utils/helpers.py
CHANGED
|
@@ -22,14 +22,13 @@ import os
|
|
|
22
22
|
import re
|
|
23
23
|
import string
|
|
24
24
|
import sys
|
|
25
|
-
import time
|
|
26
25
|
import typing
|
|
27
26
|
import warnings
|
|
28
27
|
from datetime import datetime, timezone
|
|
29
28
|
from importlib import import_module
|
|
30
29
|
from os import path
|
|
31
30
|
from types import ModuleType
|
|
32
|
-
from typing import Any,
|
|
31
|
+
from typing import Any, Optional
|
|
33
32
|
|
|
34
33
|
import anyio
|
|
35
34
|
import git
|
|
@@ -51,9 +50,15 @@ import mlrun.errors
|
|
|
51
50
|
import mlrun.utils.regex
|
|
52
51
|
import mlrun.utils.version.version
|
|
53
52
|
from mlrun.config import config
|
|
54
|
-
from mlrun.errors import err_to_str
|
|
55
53
|
|
|
56
54
|
from .logger import create_logger
|
|
55
|
+
from .retryer import ( # noqa: F401
|
|
56
|
+
AsyncRetryer,
|
|
57
|
+
Retryer,
|
|
58
|
+
create_exponential_backoff,
|
|
59
|
+
create_linear_backoff,
|
|
60
|
+
create_step_backoff,
|
|
61
|
+
)
|
|
57
62
|
|
|
58
63
|
yaml.Dumper.ignore_aliases = lambda *args: True
|
|
59
64
|
_missing = object()
|
|
@@ -276,12 +281,12 @@ def validate_v3io_stream_consumer_group(
|
|
|
276
281
|
)
|
|
277
282
|
|
|
278
283
|
|
|
279
|
-
def get_regex_list_as_string(regex_list:
|
|
284
|
+
def get_regex_list_as_string(regex_list: list) -> str:
|
|
280
285
|
"""
|
|
281
286
|
This function is used to combine a list of regex strings into a single regex,
|
|
282
287
|
with and condition between them.
|
|
283
288
|
"""
|
|
284
|
-
return "".join(["(?={regex})"
|
|
289
|
+
return "".join([f"(?={regex})" for regex in regex_list]) + ".*$"
|
|
285
290
|
|
|
286
291
|
|
|
287
292
|
def tag_name_regex_as_string() -> str:
|
|
@@ -698,7 +703,7 @@ def generate_artifact_uri(project, key, tag=None, iter=None, tree=None):
|
|
|
698
703
|
return artifact_uri
|
|
699
704
|
|
|
700
705
|
|
|
701
|
-
def extend_hub_uri_if_needed(uri) ->
|
|
706
|
+
def extend_hub_uri_if_needed(uri) -> tuple[str, bool]:
|
|
702
707
|
"""
|
|
703
708
|
Retrieve the full uri of the item's yaml in the hub.
|
|
704
709
|
|
|
@@ -787,7 +792,7 @@ def gen_html_table(header, rows=None):
|
|
|
787
792
|
def new_pipe_metadata(
|
|
788
793
|
artifact_path: str = None,
|
|
789
794
|
cleanup_ttl: int = None,
|
|
790
|
-
op_transformers:
|
|
795
|
+
op_transformers: list[typing.Callable] = None,
|
|
791
796
|
):
|
|
792
797
|
from kfp.dsl import PipelineConf
|
|
793
798
|
|
|
@@ -893,7 +898,7 @@ def get_docker_repository_or_default(repository: str) -> str:
|
|
|
893
898
|
return repository
|
|
894
899
|
|
|
895
900
|
|
|
896
|
-
def get_parsed_docker_registry() ->
|
|
901
|
+
def get_parsed_docker_registry() -> tuple[Optional[str], Optional[str]]:
|
|
897
902
|
# according to https://stackoverflow.com/questions/37861791/how-are-docker-image-names-parsed
|
|
898
903
|
docker_registry = config.httpdb.builder.docker_registry or ""
|
|
899
904
|
first_slash_index = docker_registry.find("/")
|
|
@@ -947,65 +952,27 @@ def fill_function_hash(function_dict, tag=""):
|
|
|
947
952
|
return fill_object_hash(function_dict, "hash", tag)
|
|
948
953
|
|
|
949
954
|
|
|
950
|
-
def
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
"""
|
|
954
|
-
x = 0
|
|
955
|
-
comparison = min if coefficient >= 0 else max
|
|
956
|
-
|
|
957
|
-
while True:
|
|
958
|
-
next_value = comparison(base + x * coefficient, stop_value)
|
|
959
|
-
yield next_value
|
|
960
|
-
x += 1
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
def create_step_backoff(steps=None):
|
|
964
|
-
"""
|
|
965
|
-
Create a generator of steps backoff.
|
|
966
|
-
Example: steps = [[2, 5], [20, 10], [120, None]] will produce a generator in which the first 5
|
|
967
|
-
values will be 2, the next 10 values will be 20 and the rest will be 120.
|
|
968
|
-
:param steps: a list of lists [step_value, number_of_iteration_in_this_step]
|
|
969
|
-
"""
|
|
970
|
-
steps = steps if steps is not None else [[2, 10], [10, 10], [120, None]]
|
|
971
|
-
steps = iter(steps)
|
|
972
|
-
|
|
973
|
-
# Get first step
|
|
974
|
-
step = next(steps)
|
|
975
|
-
while True:
|
|
976
|
-
current_step_value, current_step_remain = step
|
|
977
|
-
if current_step_remain == 0:
|
|
978
|
-
# No more in this step, moving on
|
|
979
|
-
step = next(steps)
|
|
980
|
-
elif current_step_remain is None:
|
|
981
|
-
# We are in the last step, staying here forever
|
|
982
|
-
yield current_step_value
|
|
983
|
-
elif current_step_remain > 0:
|
|
984
|
-
# Still more remains in this step, just reduce the remaining number
|
|
985
|
-
step[1] -= 1
|
|
986
|
-
yield current_step_value
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
def create_exponential_backoff(base=2, max_value=120, scale_factor=1):
|
|
955
|
+
def retry_until_successful(
|
|
956
|
+
backoff: int, timeout: int, logger, verbose: bool, _function, *args, **kwargs
|
|
957
|
+
):
|
|
990
958
|
"""
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
:param
|
|
994
|
-
|
|
959
|
+
Runs function with given *args and **kwargs.
|
|
960
|
+
Tries to run it until success or timeout reached (timeout is optional)
|
|
961
|
+
:param backoff: can either be a:
|
|
962
|
+
- number (int / float) that will be used as interval.
|
|
963
|
+
- generator of waiting intervals. (support next())
|
|
964
|
+
:param timeout: pass None if timeout is not wanted, number of seconds if it is
|
|
965
|
+
:param logger: a logger so we can log the failures
|
|
966
|
+
:param verbose: whether to log the failure on each retry
|
|
967
|
+
:param _function: function to run
|
|
968
|
+
:param args: functions args
|
|
969
|
+
:param kwargs: functions kwargs
|
|
970
|
+
:return: function result
|
|
995
971
|
"""
|
|
996
|
-
|
|
997
|
-
while True:
|
|
998
|
-
# This "complex" implementation (unlike the one in linear backoff) is to avoid exponent growing too fast and
|
|
999
|
-
# risking going behind max_int
|
|
1000
|
-
next_value = scale_factor * (base**exponent)
|
|
1001
|
-
if next_value < max_value:
|
|
1002
|
-
exponent += 1
|
|
1003
|
-
yield next_value
|
|
1004
|
-
else:
|
|
1005
|
-
yield max_value
|
|
972
|
+
return Retryer(backoff, timeout, logger, verbose, _function, *args, **kwargs).run()
|
|
1006
973
|
|
|
1007
974
|
|
|
1008
|
-
def
|
|
975
|
+
async def retry_until_successful_async(
|
|
1009
976
|
backoff: int, timeout: int, logger, verbose: bool, _function, *args, **kwargs
|
|
1010
977
|
):
|
|
1011
978
|
"""
|
|
@@ -1022,64 +989,15 @@ def retry_until_successful(
|
|
|
1022
989
|
:param kwargs: functions kwargs
|
|
1023
990
|
:return: function result
|
|
1024
991
|
"""
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
# Check if backoff is just a simple interval
|
|
1029
|
-
if isinstance(backoff, int) or isinstance(backoff, float):
|
|
1030
|
-
backoff = create_linear_backoff(base=backoff, coefficient=0)
|
|
1031
|
-
|
|
1032
|
-
first_interval = next(backoff)
|
|
1033
|
-
if timeout and timeout <= first_interval:
|
|
1034
|
-
logger.warning(
|
|
1035
|
-
f"Timeout ({timeout}) must be higher than backoff ({first_interval})."
|
|
1036
|
-
f" Set timeout to be higher than backoff."
|
|
1037
|
-
)
|
|
1038
|
-
|
|
1039
|
-
# If deadline was not provided or deadline not reached
|
|
1040
|
-
while timeout is None or time.time() < start_time + timeout:
|
|
1041
|
-
next_interval = first_interval or next(backoff)
|
|
1042
|
-
first_interval = None
|
|
1043
|
-
try:
|
|
1044
|
-
result = _function(*args, **kwargs)
|
|
1045
|
-
return result
|
|
1046
|
-
|
|
1047
|
-
except mlrun.errors.MLRunFatalFailureError as exc:
|
|
1048
|
-
raise exc.original_exception
|
|
1049
|
-
except Exception as exc:
|
|
1050
|
-
last_exception = exc
|
|
1051
|
-
|
|
1052
|
-
# If next interval is within allowed time period - wait on interval, abort otherwise
|
|
1053
|
-
if timeout is None or time.time() + next_interval < start_time + timeout:
|
|
1054
|
-
if logger is not None and verbose:
|
|
1055
|
-
logger.debug(
|
|
1056
|
-
f"Operation not yet successful, Retrying in {next_interval} seconds."
|
|
1057
|
-
f" exc: {err_to_str(exc)}"
|
|
1058
|
-
)
|
|
1059
|
-
|
|
1060
|
-
time.sleep(next_interval)
|
|
1061
|
-
else:
|
|
1062
|
-
break
|
|
1063
|
-
|
|
1064
|
-
if logger is not None:
|
|
1065
|
-
logger.warning(
|
|
1066
|
-
f"Operation did not complete on time. last exception: {last_exception}"
|
|
1067
|
-
)
|
|
1068
|
-
|
|
1069
|
-
raise mlrun.errors.MLRunRetryExhaustedError(
|
|
1070
|
-
f"Failed to execute command by the given deadline."
|
|
1071
|
-
f" last_exception: {last_exception},"
|
|
1072
|
-
f" function_name: {_function.__name__},"
|
|
1073
|
-
f" timeout: {timeout}"
|
|
1074
|
-
) from last_exception
|
|
992
|
+
return await AsyncRetryer(
|
|
993
|
+
backoff, timeout, logger, verbose, _function, *args, **kwargs
|
|
994
|
+
).run()
|
|
1075
995
|
|
|
1076
996
|
|
|
1077
997
|
def get_ui_url(project, uid=None):
|
|
1078
998
|
url = ""
|
|
1079
999
|
if mlrun.mlconf.resolve_ui_url():
|
|
1080
|
-
url = "{}/{}/{}/jobs"
|
|
1081
|
-
mlrun.mlconf.resolve_ui_url(), mlrun.mlconf.ui.projects_prefix, project
|
|
1082
|
-
)
|
|
1000
|
+
url = f"{mlrun.mlconf.resolve_ui_url()}/{mlrun.mlconf.ui.projects_prefix}/{project}/jobs"
|
|
1083
1001
|
if uid:
|
|
1084
1002
|
url += f"/monitor/{uid}/overview"
|
|
1085
1003
|
return url
|
|
@@ -1095,7 +1013,7 @@ def get_workflow_url(project, id=None):
|
|
|
1095
1013
|
|
|
1096
1014
|
|
|
1097
1015
|
def are_strings_in_exception_chain_messages(
|
|
1098
|
-
exception: Exception, strings_list=
|
|
1016
|
+
exception: Exception, strings_list=list[str]
|
|
1099
1017
|
) -> bool:
|
|
1100
1018
|
while exception is not None:
|
|
1101
1019
|
if any([string in str(exception) for string in strings_list]):
|
|
@@ -1275,7 +1193,7 @@ def has_timezone(timestamp):
|
|
|
1275
1193
|
return False
|
|
1276
1194
|
|
|
1277
1195
|
|
|
1278
|
-
def as_list(element: Any) ->
|
|
1196
|
+
def as_list(element: Any) -> list[Any]:
|
|
1279
1197
|
return element if isinstance(element, list) else [element]
|
|
1280
1198
|
|
|
1281
1199
|
|
|
@@ -1618,7 +1536,7 @@ def is_ecr_url(registry: str) -> bool:
|
|
|
1618
1536
|
return ".ecr." in registry and ".amazonaws.com" in registry
|
|
1619
1537
|
|
|
1620
1538
|
|
|
1621
|
-
def get_local_file_schema() ->
|
|
1539
|
+
def get_local_file_schema() -> list:
|
|
1622
1540
|
# The expression `list(string.ascii_lowercase)` generates a list of lowercase alphabets,
|
|
1623
1541
|
# which corresponds to drive letters in Windows file paths such as `C:/Windows/path`.
|
|
1624
1542
|
return ["file"] + list(string.ascii_lowercase)
|
mlrun/utils/http.py
CHANGED
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
#
|
|
15
15
|
|
|
16
16
|
import time
|
|
17
|
-
import typing
|
|
18
17
|
|
|
19
18
|
import requests
|
|
20
19
|
import requests.adapters
|
|
@@ -202,9 +201,7 @@ class HTTPSessionWithRetry(requests.Session):
|
|
|
202
201
|
def _method_retryable(self, method: str):
|
|
203
202
|
return method in self._retry_methods
|
|
204
203
|
|
|
205
|
-
def _resolve_retry_methods(
|
|
206
|
-
self, retry_on_post: bool = False
|
|
207
|
-
) -> typing.FrozenSet[str]:
|
|
204
|
+
def _resolve_retry_methods(self, retry_on_post: bool = False) -> frozenset[str]:
|
|
208
205
|
methods = urllib3.util.retry.Retry.DEFAULT_ALLOWED_METHODS
|
|
209
206
|
methods = methods.union({"PATCH"})
|
|
210
207
|
if retry_on_post:
|
mlrun/utils/logger.py
CHANGED
|
@@ -12,22 +12,48 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
import json
|
|
16
15
|
import logging
|
|
17
16
|
from enum import Enum
|
|
18
17
|
from sys import stdout
|
|
19
18
|
from traceback import format_exception
|
|
20
19
|
from typing import IO, Optional, Union
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
import orjson
|
|
22
|
+
import pydantic
|
|
23
23
|
|
|
24
|
+
from mlrun.config import config
|
|
24
25
|
|
|
25
|
-
class JSONFormatter(logging.Formatter):
|
|
26
|
-
def __init__(self):
|
|
27
|
-
super(JSONFormatter, self).__init__()
|
|
28
|
-
self._json_encoder = json.JSONEncoder()
|
|
29
26
|
|
|
30
|
-
|
|
27
|
+
class _BaseFormatter(logging.Formatter):
|
|
28
|
+
def _json_dump(self, json_object):
|
|
29
|
+
def default(obj):
|
|
30
|
+
if isinstance(obj, pydantic.BaseModel):
|
|
31
|
+
return obj.dict()
|
|
32
|
+
|
|
33
|
+
# EAFP all the way.
|
|
34
|
+
# Leave the unused "exc" in for debugging ease
|
|
35
|
+
try:
|
|
36
|
+
return obj.__log__()
|
|
37
|
+
except Exception as exc: # noqa
|
|
38
|
+
try:
|
|
39
|
+
return obj.__repr__()
|
|
40
|
+
except Exception as exc: # noqa
|
|
41
|
+
try:
|
|
42
|
+
return str(obj)
|
|
43
|
+
except Exception as exc:
|
|
44
|
+
raise TypeError from exc
|
|
45
|
+
|
|
46
|
+
return orjson.dumps(
|
|
47
|
+
json_object,
|
|
48
|
+
option=orjson.OPT_NAIVE_UTC
|
|
49
|
+
| orjson.OPT_SERIALIZE_NUMPY
|
|
50
|
+
| orjson.OPT_SORT_KEYS,
|
|
51
|
+
default=default,
|
|
52
|
+
).decode()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class JSONFormatter(_BaseFormatter):
|
|
56
|
+
def format(self, record) -> str:
|
|
31
57
|
record_with = getattr(record, "with", {})
|
|
32
58
|
if record.exc_info:
|
|
33
59
|
record_with.update(exc_info=format_exception(*record.exc_info))
|
|
@@ -38,14 +64,24 @@ class JSONFormatter(logging.Formatter):
|
|
|
38
64
|
"with": record_with,
|
|
39
65
|
}
|
|
40
66
|
|
|
41
|
-
return self.
|
|
67
|
+
return self._json_dump(record_fields)
|
|
42
68
|
|
|
43
69
|
|
|
44
|
-
class HumanReadableFormatter(
|
|
70
|
+
class HumanReadableFormatter(_BaseFormatter):
|
|
45
71
|
def format(self, record) -> str:
|
|
72
|
+
more = self._resolve_more(record)
|
|
73
|
+
return (
|
|
74
|
+
f"> {self.formatTime(record, self.datefmt)} "
|
|
75
|
+
f"[{record.levelname.lower()}] "
|
|
76
|
+
f"{record.getMessage().rstrip()}"
|
|
77
|
+
f"{more}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _resolve_more(self, record):
|
|
46
81
|
record_with = self._record_with(record)
|
|
47
|
-
|
|
48
|
-
|
|
82
|
+
record_with_encoded = self._json_dump(record_with) if record_with else ""
|
|
83
|
+
more = f": {record_with_encoded}" if record_with_encoded else ""
|
|
84
|
+
return more
|
|
49
85
|
|
|
50
86
|
def _record_with(self, record):
|
|
51
87
|
record_with = getattr(record, "with", {})
|
|
@@ -56,8 +92,7 @@ class HumanReadableFormatter(logging.Formatter):
|
|
|
56
92
|
|
|
57
93
|
class HumanReadableExtendedFormatter(HumanReadableFormatter):
|
|
58
94
|
def format(self, record) -> str:
|
|
59
|
-
|
|
60
|
-
more = f": {record_with}" if record_with else ""
|
|
95
|
+
more = self._resolve_more(record)
|
|
61
96
|
return (
|
|
62
97
|
"> "
|
|
63
98
|
f"{self.formatTime(record, self.datefmt)} "
|
|
@@ -66,7 +101,7 @@ class HumanReadableExtendedFormatter(HumanReadableFormatter):
|
|
|
66
101
|
)
|
|
67
102
|
|
|
68
103
|
|
|
69
|
-
class Logger
|
|
104
|
+
class Logger:
|
|
70
105
|
def __init__(
|
|
71
106
|
self,
|
|
72
107
|
level,
|
|
@@ -32,7 +32,7 @@ class NotificationTypes(str, enum.Enum):
|
|
|
32
32
|
slack = NotificationKind.slack.value
|
|
33
33
|
webhook = NotificationKind.webhook.value
|
|
34
34
|
|
|
35
|
-
def get_notification(self) ->
|
|
35
|
+
def get_notification(self) -> type[NotificationBase]:
|
|
36
36
|
return {
|
|
37
37
|
self.console: ConsoleNotification,
|
|
38
38
|
self.git: GitNotification,
|
|
@@ -41,7 +41,7 @@ class NotificationTypes(str, enum.Enum):
|
|
|
41
41
|
self.webhook: WebhookNotification,
|
|
42
42
|
}.get(self)
|
|
43
43
|
|
|
44
|
-
def inverse_dependencies(self) ->
|
|
44
|
+
def inverse_dependencies(self) -> list[str]:
|
|
45
45
|
"""
|
|
46
46
|
Some notifications should only run if another notification type didn't run.
|
|
47
47
|
Per given notification type, return a list of notification types that should not run in order for this
|
|
@@ -52,7 +52,7 @@ class NotificationTypes(str, enum.Enum):
|
|
|
52
52
|
}.get(self, [])
|
|
53
53
|
|
|
54
54
|
@classmethod
|
|
55
|
-
def all(cls) ->
|
|
55
|
+
def all(cls) -> list[str]:
|
|
56
56
|
return list(
|
|
57
57
|
[
|
|
58
58
|
cls.console,
|
|
@@ -23,7 +23,7 @@ class NotificationBase:
|
|
|
23
23
|
def __init__(
|
|
24
24
|
self,
|
|
25
25
|
name: str = None,
|
|
26
|
-
params:
|
|
26
|
+
params: dict[str, str] = None,
|
|
27
27
|
):
|
|
28
28
|
self.name = name
|
|
29
29
|
self.params = params or {}
|
|
@@ -49,7 +49,7 @@ class NotificationBase:
|
|
|
49
49
|
|
|
50
50
|
def load_notification(
|
|
51
51
|
self,
|
|
52
|
-
params:
|
|
52
|
+
params: dict[str, str],
|
|
53
53
|
) -> None:
|
|
54
54
|
self.params = params or {}
|
|
55
55
|
|
|
@@ -32,7 +32,7 @@ from mlrun.utils.condition_evaluator import evaluate_condition_in_separate_proce
|
|
|
32
32
|
from .notification import NotificationBase, NotificationTypes
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class _NotificationPusherBase
|
|
35
|
+
class _NotificationPusherBase:
|
|
36
36
|
def _push(
|
|
37
37
|
self, sync_push_callback: typing.Callable, async_push_callback: typing.Callable
|
|
38
38
|
):
|
|
@@ -95,15 +95,11 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
95
95
|
|
|
96
96
|
def __init__(self, runs: typing.Union[mlrun.lists.RunList, list]):
|
|
97
97
|
self._runs = runs
|
|
98
|
-
self._sync_notifications:
|
|
99
|
-
|
|
100
|
-
NotificationBase, mlrun.model.RunObject, mlrun.model.Notification
|
|
101
|
-
]
|
|
98
|
+
self._sync_notifications: list[
|
|
99
|
+
tuple[NotificationBase, mlrun.model.RunObject, mlrun.model.Notification]
|
|
102
100
|
] = []
|
|
103
|
-
self._async_notifications:
|
|
104
|
-
|
|
105
|
-
NotificationBase, mlrun.model.RunObject, mlrun.model.Notification
|
|
106
|
-
]
|
|
101
|
+
self._async_notifications: list[
|
|
102
|
+
tuple[NotificationBase, mlrun.model.RunObject, mlrun.model.Notification]
|
|
107
103
|
] = []
|
|
108
104
|
|
|
109
105
|
for run in self._runs:
|
|
@@ -401,7 +397,7 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
401
397
|
|
|
402
398
|
|
|
403
399
|
class CustomNotificationPusher(_NotificationPusherBase):
|
|
404
|
-
def __init__(self, notification_types:
|
|
400
|
+
def __init__(self, notification_types: list[str] = None):
|
|
405
401
|
notifications = {
|
|
406
402
|
notification_type: NotificationTypes(notification_type).get_notification()()
|
|
407
403
|
for notification_type in notification_types
|
|
@@ -446,7 +442,7 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
446
442
|
def add_notification(
|
|
447
443
|
self,
|
|
448
444
|
notification_type: str,
|
|
449
|
-
params:
|
|
445
|
+
params: dict[str, str] = None,
|
|
450
446
|
):
|
|
451
447
|
if notification_type in self._async_notifications:
|
|
452
448
|
self._async_notifications[notification_type].load_notification(params)
|
|
@@ -471,9 +467,7 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
471
467
|
else:
|
|
472
468
|
logger.warning(f"No notification of type {notification_type} in project")
|
|
473
469
|
|
|
474
|
-
def edit_notification(
|
|
475
|
-
self, notification_type: str, params: typing.Dict[str, str] = None
|
|
476
|
-
):
|
|
470
|
+
def edit_notification(self, notification_type: str, params: dict[str, str] = None):
|
|
477
471
|
self.remove_notification(notification_type)
|
|
478
472
|
self.add_notification(notification_type, params)
|
|
479
473
|
|