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.

Files changed (199) hide show
  1. mlrun/__main__.py +3 -3
  2. mlrun/api/schemas/__init__.py +1 -1
  3. mlrun/artifacts/base.py +11 -6
  4. mlrun/artifacts/dataset.py +2 -2
  5. mlrun/artifacts/model.py +30 -24
  6. mlrun/artifacts/plots.py +2 -2
  7. mlrun/common/db/sql_session.py +5 -3
  8. mlrun/common/helpers.py +1 -2
  9. mlrun/common/schemas/artifact.py +3 -3
  10. mlrun/common/schemas/auth.py +3 -3
  11. mlrun/common/schemas/background_task.py +1 -1
  12. mlrun/common/schemas/client_spec.py +1 -1
  13. mlrun/common/schemas/feature_store.py +16 -16
  14. mlrun/common/schemas/frontend_spec.py +7 -7
  15. mlrun/common/schemas/function.py +1 -1
  16. mlrun/common/schemas/hub.py +4 -9
  17. mlrun/common/schemas/memory_reports.py +2 -2
  18. mlrun/common/schemas/model_monitoring/grafana.py +4 -4
  19. mlrun/common/schemas/model_monitoring/model_endpoints.py +14 -15
  20. mlrun/common/schemas/notification.py +4 -4
  21. mlrun/common/schemas/object.py +2 -2
  22. mlrun/common/schemas/pipeline.py +1 -1
  23. mlrun/common/schemas/project.py +3 -3
  24. mlrun/common/schemas/runtime_resource.py +8 -12
  25. mlrun/common/schemas/schedule.py +3 -3
  26. mlrun/common/schemas/tag.py +1 -2
  27. mlrun/common/schemas/workflow.py +2 -2
  28. mlrun/config.py +8 -4
  29. mlrun/data_types/to_pandas.py +1 -3
  30. mlrun/datastore/base.py +0 -28
  31. mlrun/datastore/datastore_profile.py +9 -9
  32. mlrun/datastore/filestore.py +0 -1
  33. mlrun/datastore/google_cloud_storage.py +1 -1
  34. mlrun/datastore/sources.py +7 -11
  35. mlrun/datastore/spark_utils.py +1 -2
  36. mlrun/datastore/targets.py +31 -31
  37. mlrun/datastore/utils.py +4 -6
  38. mlrun/datastore/v3io.py +70 -46
  39. mlrun/db/base.py +22 -23
  40. mlrun/db/httpdb.py +34 -34
  41. mlrun/db/nopdb.py +19 -19
  42. mlrun/errors.py +1 -1
  43. mlrun/execution.py +4 -4
  44. mlrun/feature_store/api.py +20 -21
  45. mlrun/feature_store/common.py +1 -1
  46. mlrun/feature_store/feature_set.py +28 -32
  47. mlrun/feature_store/feature_vector.py +24 -27
  48. mlrun/feature_store/retrieval/base.py +7 -7
  49. mlrun/feature_store/retrieval/conversion.py +2 -4
  50. mlrun/feature_store/steps.py +7 -15
  51. mlrun/features.py +5 -7
  52. mlrun/frameworks/_common/artifacts_library.py +9 -9
  53. mlrun/frameworks/_common/mlrun_interface.py +5 -5
  54. mlrun/frameworks/_common/model_handler.py +48 -48
  55. mlrun/frameworks/_common/plan.py +2 -3
  56. mlrun/frameworks/_common/producer.py +3 -4
  57. mlrun/frameworks/_common/utils.py +5 -5
  58. mlrun/frameworks/_dl_common/loggers/logger.py +6 -7
  59. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +9 -9
  60. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +16 -35
  61. mlrun/frameworks/_ml_common/artifacts_library.py +1 -2
  62. mlrun/frameworks/_ml_common/loggers/logger.py +3 -4
  63. mlrun/frameworks/_ml_common/loggers/mlrun_logger.py +4 -5
  64. mlrun/frameworks/_ml_common/model_handler.py +24 -24
  65. mlrun/frameworks/_ml_common/pkl_model_server.py +2 -2
  66. mlrun/frameworks/_ml_common/plan.py +1 -1
  67. mlrun/frameworks/_ml_common/plans/calibration_curve_plan.py +2 -3
  68. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +2 -3
  69. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  70. mlrun/frameworks/_ml_common/plans/feature_importance_plan.py +3 -3
  71. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  72. mlrun/frameworks/_ml_common/utils.py +4 -4
  73. mlrun/frameworks/auto_mlrun/auto_mlrun.py +7 -7
  74. mlrun/frameworks/huggingface/model_server.py +4 -4
  75. mlrun/frameworks/lgbm/__init__.py +32 -32
  76. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -5
  77. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -5
  78. mlrun/frameworks/lgbm/mlrun_interfaces/booster_mlrun_interface.py +1 -3
  79. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +6 -6
  80. mlrun/frameworks/lgbm/model_handler.py +9 -9
  81. mlrun/frameworks/lgbm/model_server.py +6 -6
  82. mlrun/frameworks/lgbm/utils.py +5 -5
  83. mlrun/frameworks/onnx/dataset.py +8 -8
  84. mlrun/frameworks/onnx/mlrun_interface.py +3 -3
  85. mlrun/frameworks/onnx/model_handler.py +6 -6
  86. mlrun/frameworks/onnx/model_server.py +7 -7
  87. mlrun/frameworks/parallel_coordinates.py +2 -2
  88. mlrun/frameworks/pytorch/__init__.py +16 -16
  89. mlrun/frameworks/pytorch/callbacks/callback.py +4 -5
  90. mlrun/frameworks/pytorch/callbacks/logging_callback.py +17 -17
  91. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +11 -11
  92. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +23 -29
  93. mlrun/frameworks/pytorch/callbacks_handler.py +38 -38
  94. mlrun/frameworks/pytorch/mlrun_interface.py +20 -20
  95. mlrun/frameworks/pytorch/model_handler.py +17 -17
  96. mlrun/frameworks/pytorch/model_server.py +7 -7
  97. mlrun/frameworks/sklearn/__init__.py +12 -12
  98. mlrun/frameworks/sklearn/estimator.py +4 -4
  99. mlrun/frameworks/sklearn/metrics_library.py +14 -14
  100. mlrun/frameworks/sklearn/mlrun_interface.py +3 -6
  101. mlrun/frameworks/sklearn/model_handler.py +2 -2
  102. mlrun/frameworks/tf_keras/__init__.py +5 -5
  103. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +14 -14
  104. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +11 -11
  105. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +19 -23
  106. mlrun/frameworks/tf_keras/mlrun_interface.py +7 -9
  107. mlrun/frameworks/tf_keras/model_handler.py +14 -14
  108. mlrun/frameworks/tf_keras/model_server.py +6 -6
  109. mlrun/frameworks/xgboost/__init__.py +12 -12
  110. mlrun/frameworks/xgboost/model_handler.py +6 -6
  111. mlrun/k8s_utils.py +4 -5
  112. mlrun/kfpops.py +2 -2
  113. mlrun/launcher/base.py +10 -10
  114. mlrun/launcher/local.py +8 -8
  115. mlrun/launcher/remote.py +7 -7
  116. mlrun/lists.py +3 -4
  117. mlrun/model.py +205 -55
  118. mlrun/model_monitoring/api.py +21 -24
  119. mlrun/model_monitoring/application.py +4 -4
  120. mlrun/model_monitoring/batch.py +17 -17
  121. mlrun/model_monitoring/controller.py +2 -1
  122. mlrun/model_monitoring/features_drift_table.py +44 -31
  123. mlrun/model_monitoring/prometheus.py +1 -4
  124. mlrun/model_monitoring/stores/kv_model_endpoint_store.py +11 -13
  125. mlrun/model_monitoring/stores/model_endpoint_store.py +9 -11
  126. mlrun/model_monitoring/stores/models/__init__.py +2 -2
  127. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +11 -13
  128. mlrun/model_monitoring/stream_processing.py +16 -34
  129. mlrun/model_monitoring/tracking_policy.py +2 -1
  130. mlrun/package/__init__.py +6 -6
  131. mlrun/package/context_handler.py +5 -5
  132. mlrun/package/packager.py +7 -7
  133. mlrun/package/packagers/default_packager.py +6 -6
  134. mlrun/package/packagers/numpy_packagers.py +15 -15
  135. mlrun/package/packagers/pandas_packagers.py +5 -5
  136. mlrun/package/packagers/python_standard_library_packagers.py +10 -10
  137. mlrun/package/packagers_manager.py +18 -23
  138. mlrun/package/utils/_formatter.py +4 -4
  139. mlrun/package/utils/_pickler.py +2 -2
  140. mlrun/package/utils/_supported_format.py +4 -4
  141. mlrun/package/utils/log_hint_utils.py +2 -2
  142. mlrun/package/utils/type_hint_utils.py +4 -9
  143. mlrun/platforms/other.py +1 -2
  144. mlrun/projects/operations.py +5 -5
  145. mlrun/projects/pipelines.py +9 -9
  146. mlrun/projects/project.py +58 -46
  147. mlrun/render.py +1 -1
  148. mlrun/run.py +9 -9
  149. mlrun/runtimes/__init__.py +7 -4
  150. mlrun/runtimes/base.py +20 -23
  151. mlrun/runtimes/constants.py +5 -5
  152. mlrun/runtimes/daskjob.py +8 -8
  153. mlrun/runtimes/databricks_job/databricks_cancel_task.py +1 -1
  154. mlrun/runtimes/databricks_job/databricks_runtime.py +7 -7
  155. mlrun/runtimes/function_reference.py +1 -1
  156. mlrun/runtimes/local.py +1 -1
  157. mlrun/runtimes/mpijob/abstract.py +1 -2
  158. mlrun/runtimes/nuclio/__init__.py +20 -0
  159. mlrun/runtimes/{function.py → nuclio/function.py} +15 -16
  160. mlrun/runtimes/{nuclio.py → nuclio/nuclio.py} +6 -6
  161. mlrun/runtimes/{serving.py → nuclio/serving.py} +13 -12
  162. mlrun/runtimes/pod.py +95 -48
  163. mlrun/runtimes/remotesparkjob.py +1 -1
  164. mlrun/runtimes/sparkjob/spark3job.py +50 -33
  165. mlrun/runtimes/utils.py +1 -2
  166. mlrun/secrets.py +3 -3
  167. mlrun/serving/remote.py +0 -4
  168. mlrun/serving/routers.py +6 -6
  169. mlrun/serving/server.py +4 -4
  170. mlrun/serving/states.py +29 -0
  171. mlrun/serving/utils.py +3 -3
  172. mlrun/serving/v1_serving.py +6 -7
  173. mlrun/serving/v2_serving.py +50 -8
  174. mlrun/track/tracker_manager.py +3 -3
  175. mlrun/track/trackers/mlflow_tracker.py +1 -2
  176. mlrun/utils/async_http.py +5 -7
  177. mlrun/utils/azure_vault.py +1 -1
  178. mlrun/utils/clones.py +1 -2
  179. mlrun/utils/condition_evaluator.py +3 -3
  180. mlrun/utils/db.py +3 -3
  181. mlrun/utils/helpers.py +37 -119
  182. mlrun/utils/http.py +1 -4
  183. mlrun/utils/logger.py +49 -14
  184. mlrun/utils/notifications/notification/__init__.py +3 -3
  185. mlrun/utils/notifications/notification/base.py +2 -2
  186. mlrun/utils/notifications/notification/ipython.py +1 -1
  187. mlrun/utils/notifications/notification_pusher.py +8 -14
  188. mlrun/utils/retryer.py +207 -0
  189. mlrun/utils/singleton.py +1 -1
  190. mlrun/utils/v3io_clients.py +2 -3
  191. mlrun/utils/version/version.json +2 -2
  192. mlrun/utils/version/version.py +2 -6
  193. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/METADATA +9 -9
  194. mlrun-1.7.0rc2.dist-info/RECORD +315 -0
  195. mlrun-1.6.0rc35.dist-info/RECORD +0 -313
  196. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/LICENSE +0 -0
  197. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/WHEEL +0 -0
  198. {mlrun-1.6.0rc35.dist-info → mlrun-1.7.0rc2.dist-info}/entry_points.txt +0 -0
  199. {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, List, Optional, Tuple
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: List) -> str:
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})".format(regex=regex) for regex in regex_list]) + ".*$"
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) -> Tuple[str, bool]:
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: typing.List[typing.Callable] = None,
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() -> Tuple[Optional[str], Optional[str]]:
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 create_linear_backoff(base=2, coefficient=2, stop_value=120):
951
- """
952
- Create a generator of linear backoff. Check out usage example in test_helpers.py
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
- Create a generator of exponential backoff. Check out usage example in test_helpers.py
992
- :param base: exponent base
993
- :param max_value: max limit on the result
994
- :param scale_factor: factor to be used as linear scaling coefficient
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
- exponent = 1
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 retry_until_successful(
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
- start_time = time.time()
1026
- last_exception = None
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".format(
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=typing.List[str]
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) -> List[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() -> List:
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
- from mlrun.config import config
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
- def format(self, record):
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._json_encoder.encode(record_fields)
67
+ return self._json_dump(record_fields)
42
68
 
43
69
 
44
- class HumanReadableFormatter(logging.Formatter):
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
- more = f": {record_with}" if record_with else ""
48
- return f"> {self.formatTime(record, self.datefmt)} [{record.levelname.lower()}] {record.getMessage()}{more}"
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
- record_with = self._record_with(record)
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(object):
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) -> typing.Type[NotificationBase]:
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) -> typing.List[str]:
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) -> typing.List[str]:
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: typing.Dict[str, str] = None,
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: typing.Dict[str, str],
52
+ params: dict[str, str],
53
53
  ) -> None:
54
54
  self.params = params or {}
55
55
 
@@ -29,7 +29,7 @@ class IPythonNotification(NotificationBase):
29
29
  def __init__(
30
30
  self,
31
31
  name: str = None,
32
- params: typing.Dict[str, str] = None,
32
+ params: dict[str, str] = None,
33
33
  ):
34
34
  super().__init__(name, params)
35
35
  self._ipython = None
@@ -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(object):
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: typing.List[
99
- typing.Tuple[
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: typing.List[
104
- typing.Tuple[
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: typing.List[str] = None):
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: typing.Dict[str, str] = None,
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