mlrun 1.3.1rc5__py3-none-any.whl → 1.4.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 (67) hide show
  1. mlrun/__main__.py +57 -4
  2. mlrun/api/api/endpoints/marketplace.py +57 -4
  3. mlrun/api/api/endpoints/runs.py +2 -0
  4. mlrun/api/api/utils.py +102 -0
  5. mlrun/api/crud/__init__.py +1 -0
  6. mlrun/api/crud/marketplace.py +133 -44
  7. mlrun/api/crud/notifications.py +80 -0
  8. mlrun/api/crud/runs.py +2 -0
  9. mlrun/api/crud/secrets.py +1 -0
  10. mlrun/api/db/base.py +32 -0
  11. mlrun/api/db/session.py +3 -11
  12. mlrun/api/db/sqldb/db.py +162 -1
  13. mlrun/api/db/sqldb/models/models_mysql.py +41 -0
  14. mlrun/api/db/sqldb/models/models_sqlite.py +35 -0
  15. mlrun/api/main.py +54 -1
  16. mlrun/api/migrations_mysql/versions/c905d15bd91d_notifications.py +70 -0
  17. mlrun/api/migrations_sqlite/versions/959ae00528ad_notifications.py +61 -0
  18. mlrun/api/schemas/__init__.py +1 -0
  19. mlrun/api/schemas/marketplace.py +18 -8
  20. mlrun/api/{db/filedb/__init__.py → schemas/notification.py} +17 -1
  21. mlrun/api/utils/singletons/db.py +8 -14
  22. mlrun/builder.py +37 -26
  23. mlrun/config.py +12 -2
  24. mlrun/data_types/spark.py +9 -2
  25. mlrun/datastore/base.py +10 -1
  26. mlrun/datastore/sources.py +1 -1
  27. mlrun/db/__init__.py +6 -4
  28. mlrun/db/base.py +1 -2
  29. mlrun/db/httpdb.py +32 -6
  30. mlrun/db/nopdb.py +463 -0
  31. mlrun/db/sqldb.py +47 -7
  32. mlrun/execution.py +3 -0
  33. mlrun/feature_store/api.py +26 -12
  34. mlrun/feature_store/common.py +1 -1
  35. mlrun/feature_store/steps.py +110 -13
  36. mlrun/k8s_utils.py +10 -0
  37. mlrun/model.py +43 -0
  38. mlrun/projects/operations.py +5 -2
  39. mlrun/projects/pipelines.py +4 -3
  40. mlrun/projects/project.py +50 -10
  41. mlrun/run.py +5 -4
  42. mlrun/runtimes/__init__.py +2 -6
  43. mlrun/runtimes/base.py +82 -31
  44. mlrun/runtimes/function.py +22 -0
  45. mlrun/runtimes/kubejob.py +10 -8
  46. mlrun/runtimes/serving.py +1 -1
  47. mlrun/runtimes/sparkjob/__init__.py +0 -1
  48. mlrun/runtimes/sparkjob/abstract.py +0 -2
  49. mlrun/serving/states.py +2 -2
  50. mlrun/utils/helpers.py +1 -1
  51. mlrun/utils/notifications/notification/__init__.py +1 -1
  52. mlrun/utils/notifications/notification/base.py +14 -13
  53. mlrun/utils/notifications/notification/console.py +6 -3
  54. mlrun/utils/notifications/notification/git.py +19 -12
  55. mlrun/utils/notifications/notification/ipython.py +6 -3
  56. mlrun/utils/notifications/notification/slack.py +13 -12
  57. mlrun/utils/notifications/notification_pusher.py +185 -37
  58. mlrun/utils/version/version.json +2 -2
  59. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/METADATA +6 -2
  60. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/RECORD +64 -63
  61. mlrun/api/db/filedb/db.py +0 -518
  62. mlrun/db/filedb.py +0 -899
  63. mlrun/runtimes/sparkjob/spark2job.py +0 -59
  64. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/LICENSE +0 -0
  65. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/WHEEL +0 -0
  66. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/entry_points.txt +0 -0
  67. {mlrun-1.3.1rc5.dist-info → mlrun-1.4.0rc2.dist-info}/top_level.txt +0 -0
mlrun/runtimes/base.py CHANGED
@@ -15,7 +15,6 @@ import enum
15
15
  import getpass
16
16
  import http
17
17
  import os.path
18
- import shlex
19
18
  import traceback
20
19
  import typing
21
20
  import uuid
@@ -33,8 +32,11 @@ from kubernetes.client.rest import ApiException
33
32
  from nuclio.build import mlrun_footer
34
33
  from sqlalchemy.orm import Session
35
34
 
35
+ import mlrun.api.db.sqldb.session
36
+ import mlrun.api.utils.singletons.db
36
37
  import mlrun.errors
37
38
  import mlrun.utils.helpers
39
+ import mlrun.utils.notifications
38
40
  import mlrun.utils.regex
39
41
  from mlrun.api import schemas
40
42
  from mlrun.api.constants import LogSources
@@ -345,6 +347,7 @@ class BaseRuntime(ModelObj):
345
347
  local_code_path=None,
346
348
  auto_build=None,
347
349
  param_file_secrets: Dict[str, str] = None,
350
+ notifications: List[mlrun.model.Notification] = None,
348
351
  returns: Optional[List[Union[str, Dict[str, str]]]] = None,
349
352
  ) -> RunObject:
350
353
  """
@@ -379,6 +382,7 @@ class BaseRuntime(ModelObj):
379
382
  function run, use only if you dont plan on changing the build config between runs
380
383
  :param param_file_secrets: dictionary of secrets to be used only for accessing the hyper-param parameter file.
381
384
  These secrets are only used locally and will not be stored anywhere
385
+ :param notifications: list of notifications to push when the run is completed
382
386
  :param returns: List of log hints - configurations for how to log the returning values from the handler's run
383
387
  (as artifacts or results). The list's length must be equal to the amount of returning objects. A
384
388
  log hint may be given as:
@@ -408,7 +412,7 @@ class BaseRuntime(ModelObj):
408
412
  raise mlrun.errors.MLRunInvalidArgumentError(
409
413
  "local and schedule cannot be used together"
410
414
  )
411
- return self._run_local(
415
+ result = self._run_local(
412
416
  run,
413
417
  local_code_path,
414
418
  project,
@@ -419,7 +423,10 @@ class BaseRuntime(ModelObj):
419
423
  inputs,
420
424
  returns,
421
425
  artifact_path,
426
+ notifications=notifications,
422
427
  )
428
+ self._save_or_push_notifications(result, local)
429
+ return result
423
430
 
424
431
  run = self._enrich_run(
425
432
  run,
@@ -436,6 +443,7 @@ class BaseRuntime(ModelObj):
436
443
  out_path,
437
444
  artifact_path,
438
445
  workdir,
446
+ notifications,
439
447
  )
440
448
  self._validate_output_path(run)
441
449
  db = self._get_db()
@@ -533,6 +541,8 @@ class BaseRuntime(ModelObj):
533
541
  last_err = err
534
542
  result = self._update_run_state(task=run, err=err)
535
543
 
544
+ self._save_or_push_notifications(run)
545
+
536
546
  self._post_run(result, execution) # hook for runtime specific cleanup
537
547
 
538
548
  return self._wrap_run_result(result, run, schedule=schedule, err=last_err)
@@ -630,6 +640,7 @@ class BaseRuntime(ModelObj):
630
640
  inputs,
631
641
  returns,
632
642
  artifact_path,
643
+ notifications: List[mlrun.model.Notification] = None,
633
644
  ):
634
645
  # allow local run simulation with a flip of a flag
635
646
  command = self
@@ -650,6 +661,7 @@ class BaseRuntime(ModelObj):
650
661
  artifact_path=artifact_path,
651
662
  mode=self.spec.mode,
652
663
  allow_empty_resources=self.spec.allow_empty_resources,
664
+ notifications=notifications,
653
665
  returns=returns,
654
666
  )
655
667
 
@@ -688,6 +700,7 @@ class BaseRuntime(ModelObj):
688
700
  out_path,
689
701
  artifact_path,
690
702
  workdir,
703
+ notifications: List[mlrun.model.Notification] = None,
691
704
  ):
692
705
  runspec.spec.handler = (
693
706
  handler or runspec.spec.handler or self.spec.default_handler or ""
@@ -789,6 +802,8 @@ class BaseRuntime(ModelObj):
789
802
  runspec.spec.output_path = mlrun.utils.helpers.fill_artifact_path_template(
790
803
  runspec.spec.output_path, runspec.metadata.project
791
804
  )
805
+
806
+ runspec.spec.notifications = notifications or runspec.spec.notifications or []
792
807
  return runspec
793
808
 
794
809
  def _submit_job(self, run: RunObject, schedule, db, watch):
@@ -1049,6 +1064,47 @@ class BaseRuntime(ModelObj):
1049
1064
 
1050
1065
  return resp
1051
1066
 
1067
+ def _save_or_push_notifications(self, runobj: RunObject, local: bool = False):
1068
+
1069
+ if not runobj.spec.notifications:
1070
+ logger.debug(
1071
+ "No notifications to push for run", run_uid=runobj.metadata.uid
1072
+ )
1073
+ return
1074
+
1075
+ # TODO: add support for other notifications per run iteration
1076
+ if runobj.metadata.iteration and runobj.metadata.iteration > 0:
1077
+ logger.debug(
1078
+ "Notifications per iteration are not supported, skipping",
1079
+ run_uid=runobj.metadata.uid,
1080
+ )
1081
+ return
1082
+
1083
+ # If the run is remote, and we are in the SDK, we let the api deal with the notifications
1084
+ # so there's nothing to do here.
1085
+ # Otherwise, we continue on.
1086
+ if is_running_as_api():
1087
+
1088
+ # import here to avoid circular imports and to avoid importing api requirements
1089
+ from mlrun.api.crud import Notifications
1090
+
1091
+ # If in the api server, we can assume that watch=False, so we save notification
1092
+ # configs to the DB, for the run monitor to later pick up and push.
1093
+ session = mlrun.api.db.sqldb.session.create_session()
1094
+ Notifications().store_run_notifications(
1095
+ session,
1096
+ runobj.spec.notifications,
1097
+ runobj.metadata.uid,
1098
+ runobj.metadata.project,
1099
+ )
1100
+
1101
+ elif local:
1102
+ # If the run is local, we can assume that watch=True, therefore this code runs
1103
+ # once the run is completed, and we can just push the notifications.
1104
+ # TODO: add store_notifications API endpoint so we can store notifications pushed from the
1105
+ # SDK for documentation purposes.
1106
+ mlrun.utils.notifications.NotificationPusher([runobj]).push()
1107
+
1052
1108
  def _force_handler(self, handler):
1053
1109
  if not handler:
1054
1110
  raise RunError(f"handler must be provided for {self.kind} runtime")
@@ -1220,15 +1276,19 @@ class BaseRuntime(ModelObj):
1220
1276
  :param verify_base_image: verify that the base image is configured
1221
1277
  :return: function object
1222
1278
  """
1223
- encoded_requirements = self._encode_requirements(requirements)
1224
- commands = self.spec.build.commands or [] if not overwrite else []
1225
- new_command = f"python -m pip install {encoded_requirements}"
1226
- # make sure we dont append the same line twice
1227
- if new_command not in commands:
1228
- commands.append(new_command)
1229
- self.spec.build.commands = commands
1279
+ resolved_requirements = self._resolve_requirements(requirements)
1280
+ requirements = self.spec.build.requirements or [] if not overwrite else []
1281
+
1282
+ # make sure we don't append the same line twice
1283
+ for requirement in resolved_requirements:
1284
+ if requirement not in requirements:
1285
+ requirements.append(requirement)
1286
+
1287
+ self.spec.build.requirements = requirements
1288
+
1230
1289
  if verify_base_image:
1231
1290
  self.verify_base_image()
1291
+
1232
1292
  return self
1233
1293
 
1234
1294
  def with_commands(
@@ -1270,8 +1330,10 @@ class BaseRuntime(ModelObj):
1270
1330
 
1271
1331
  def verify_base_image(self):
1272
1332
  build = self.spec.build
1273
- require_build = build.commands or (
1274
- build.source and not build.load_source_on_run
1333
+ require_build = (
1334
+ build.commands
1335
+ or build.requirements
1336
+ or (build.source and not build.load_source_on_run)
1275
1337
  )
1276
1338
  image = self.spec.image
1277
1339
  # we allow users to not set an image, in that case we'll use the default
@@ -1396,15 +1458,16 @@ class BaseRuntime(ModelObj):
1396
1458
  line += f", default={p['default']}"
1397
1459
  print(" " + line)
1398
1460
 
1399
- def _encode_requirements(self, requirements_to_encode):
1400
-
1461
+ @staticmethod
1462
+ def _resolve_requirements(requirements_to_resolve: typing.Union[str, list]) -> list:
1401
1463
  # if a string, read the file then encode
1402
- if isinstance(requirements_to_encode, str):
1403
- with open(requirements_to_encode, "r") as fp:
1404
- requirements_to_encode = fp.read().splitlines()
1464
+ if isinstance(requirements_to_resolve, str):
1465
+ with open(requirements_to_resolve, "r") as fp:
1466
+ requirements_to_resolve = fp.read().splitlines()
1405
1467
 
1406
1468
  requirements = []
1407
- for requirement in requirements_to_encode:
1469
+ for requirement in requirements_to_resolve:
1470
+ # clean redundant leading and trailing whitespaces
1408
1471
  requirement = requirement.strip()
1409
1472
 
1410
1473
  # ignore empty lines
@@ -1417,21 +1480,9 @@ class BaseRuntime(ModelObj):
1417
1480
  if len(inline_comment) > 1:
1418
1481
  requirement = inline_comment[0].strip()
1419
1482
 
1420
- # -r / --requirement are flags and should not be escaped
1421
- # we allow such flags (could be passed within the requirements.txt file) and do not
1422
- # try to open the file and include its content since it might be a remote file
1423
- # given on the base image.
1424
- for req_flag in ["-r", "--requirement"]:
1425
- if requirement.startswith(req_flag):
1426
- requirement = requirement[len(req_flag) :].strip()
1427
- requirements.append(req_flag)
1428
- break
1429
-
1430
- # wrap in single quote to ensure that the requirement is treated as a single string
1431
- # quote the requirement to avoid issues with special characters, double quotes, etc.
1432
- requirements.append(shlex.quote(requirement))
1483
+ requirements.append(requirement)
1433
1484
 
1434
- return " ".join(requirements)
1485
+ return requirements
1435
1486
 
1436
1487
  def _validate_output_path(self, run):
1437
1488
  if is_local(run.spec.output_path):
@@ -14,6 +14,7 @@
14
14
 
15
15
  import asyncio
16
16
  import json
17
+ import shlex
17
18
  import typing
18
19
  import warnings
19
20
  from base64 import b64encode
@@ -1389,6 +1390,27 @@ def compile_function_config(
1389
1390
  config=function.spec.config,
1390
1391
  )
1391
1392
  nuclio_spec.cmd = function.spec.build.commands or []
1393
+
1394
+ if function.spec.build.requirements:
1395
+ resolved_requirements = []
1396
+ # wrap in single quote to ensure that the requirement is treated as a single string
1397
+ # quote the requirement to avoid issues with special characters, double quotes, etc.
1398
+ for requirement in function.spec.build.requirements:
1399
+ # -r / --requirement are flags and should not be escaped
1400
+ # we allow such flags (could be passed within the requirements.txt file) and do not
1401
+ # try to open the file and include its content since it might be a remote file
1402
+ # given on the base image.
1403
+ for req_flag in ["-r", "--requirement"]:
1404
+ if requirement.startswith(req_flag):
1405
+ requirement = requirement[len(req_flag) :].strip()
1406
+ resolved_requirements.append(req_flag)
1407
+ break
1408
+
1409
+ resolved_requirements.append(shlex.quote(requirement))
1410
+
1411
+ encoded_requirements = " ".join(resolved_requirements)
1412
+ nuclio_spec.cmd.append(f"python -m pip install {encoded_requirements}")
1413
+
1392
1414
  project = function.metadata.project or "default"
1393
1415
  tag = function.metadata.tag
1394
1416
  handler = function.spec.function_handler
mlrun/runtimes/kubejob.py CHANGED
@@ -137,16 +137,12 @@ class KubejobRuntime(KubeResource):
137
137
  self.spec.build.image = image
138
138
  if base_image:
139
139
  self.spec.build.base_image = base_image
140
- # if overwrite and requirements or commands passed, clear the existing commands
141
- # (requirements are added to the commands parameter)
142
- if (requirements or commands) and overwrite:
143
- self.spec.build.commands = None
140
+ if commands:
141
+ self.with_commands(commands, overwrite=overwrite, verify_base_image=False)
144
142
  if requirements:
145
143
  self.with_requirements(
146
- requirements, overwrite=False, verify_base_image=False
144
+ requirements, overwrite=overwrite, verify_base_image=False
147
145
  )
148
- if commands:
149
- self.with_commands(commands, overwrite=False, verify_base_image=False)
150
146
  if extra:
151
147
  self.spec.build.extra = extra
152
148
  if secret is not None:
@@ -198,7 +194,13 @@ class KubejobRuntime(KubeResource):
198
194
  or "/mlrun/" in build.base_image
199
195
  )
200
196
 
201
- if not build.source and not build.commands and not build.extra and with_mlrun:
197
+ if (
198
+ not build.source
199
+ and not build.commands
200
+ and not build.requirements
201
+ and not build.extra
202
+ and with_mlrun
203
+ ):
202
204
  logger.info(
203
205
  "running build to add mlrun package, set "
204
206
  "with_mlrun=False to skip if its already in the image"
mlrun/runtimes/serving.py CHANGED
@@ -319,7 +319,7 @@ class ServingRuntime(RemoteRuntime):
319
319
  example::
320
320
 
321
321
  # initialize a new serving function
322
- serving_fn = mlrun.import_function("hub://v2_model_server", new_name="serving")
322
+ serving_fn = mlrun.import_function("hub://v2-model-server", new_name="serving")
323
323
  # apply model monitoring and set monitoring batch job to run every 3 hours
324
324
  tracking_policy = {'default_batch_intervals':"0 */3 * * *"}
325
325
  serving_fn.set_tracking(tracking_policy=tracking_policy)
@@ -15,5 +15,4 @@
15
15
  # flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
16
16
 
17
17
  from .abstract import SparkRuntimeHandler
18
- from .spark2job import Spark2Runtime
19
18
  from .spark3job import Spark3Runtime
@@ -963,8 +963,6 @@ class SparkRuntimeHandler(BaseRuntimeHandler):
963
963
  """
964
964
  Handling config maps deletion
965
965
  """
966
- if grace_period is None:
967
- grace_period = config.runtime_resources_deletion_grace_period
968
966
  uids = []
969
967
  for crd_dict in deleted_resources:
970
968
  uid = crd_dict["metadata"].get("labels", {}).get("mlrun/uid", None)
mlrun/serving/states.py CHANGED
@@ -28,7 +28,7 @@ from ..errors import MLRunInvalidArgumentError, err_to_str
28
28
  from ..model import ModelObj, ObjectDict
29
29
  from ..platforms.iguazio import parse_path
30
30
  from ..utils import get_class, get_function
31
- from .utils import _extract_input_data, _update_result_body
31
+ from .utils import StepToDict, _extract_input_data, _update_result_body
32
32
 
33
33
  callable_prefix = "_"
34
34
  path_splitter = "/"
@@ -279,7 +279,7 @@ class BaseStep(ModelObj):
279
279
 
280
280
  def to(
281
281
  self,
282
- class_name: Union[str, type] = None,
282
+ class_name: Union[str, StepToDict] = None,
283
283
  name: str = None,
284
284
  handler: str = None,
285
285
  graph_shape: str = None,
mlrun/utils/helpers.py CHANGED
@@ -1000,7 +1000,7 @@ def create_class(pkg_class: str):
1000
1000
  return class_
1001
1001
 
1002
1002
 
1003
- def create_function(pkg_func: list):
1003
+ def create_function(pkg_func: str):
1004
1004
  """Create a function from a package.module.function string
1005
1005
 
1006
1006
  :param pkg_func: full function location,
@@ -15,7 +15,7 @@
15
15
  import enum
16
16
  import typing
17
17
 
18
- from .base import NotificationBase, NotificationSeverity # noqa
18
+ from .base import NotificationBase
19
19
  from .console import ConsoleNotification
20
20
  from .git import GitNotification
21
21
  from .ipython import IPythonNotification
@@ -12,35 +12,31 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- import enum
16
15
  import typing
17
16
 
17
+ import mlrun.api.schemas
18
18
  import mlrun.lists
19
19
 
20
20
 
21
- class NotificationSeverity(str, enum.Enum):
22
- INFO = "info"
23
- DEBUG = "debug"
24
- VERBOSE = "verbose"
25
- WARNING = "warning"
26
- ERROR = "error"
27
-
28
-
29
21
  class NotificationBase:
30
22
  def __init__(
31
23
  self,
24
+ name: str = None,
32
25
  params: typing.Dict[str, str] = None,
33
26
  ):
27
+ self.name = name
34
28
  self.params = params or {}
35
29
 
36
30
  @property
37
31
  def active(self) -> bool:
38
32
  return True
39
33
 
40
- def send(
34
+ def push(
41
35
  self,
42
36
  message: str,
43
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
37
+ severity: typing.Union[
38
+ mlrun.api.schemas.NotificationSeverity, str
39
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
44
40
  runs: typing.Union[mlrun.lists.RunList, list] = None,
45
41
  custom_html: str = None,
46
42
  ):
@@ -52,16 +48,21 @@ class NotificationBase:
52
48
  ) -> None:
53
49
  self.params = params or {}
54
50
 
55
- @staticmethod
56
51
  def _get_html(
52
+ self,
57
53
  message: str,
58
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
54
+ severity: typing.Union[
55
+ mlrun.api.schemas.NotificationSeverity, str
56
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
59
57
  runs: typing.Union[mlrun.lists.RunList, list] = None,
60
58
  custom_html: str = None,
61
59
  ) -> str:
62
60
  if custom_html:
63
61
  return custom_html
64
62
 
63
+ if self.name:
64
+ message = f"{self.name}: {message}"
65
+
65
66
  if not runs:
66
67
  return f"[{severity}] {message}"
67
68
 
@@ -16,10 +16,11 @@ import typing
16
16
 
17
17
  import tabulate
18
18
 
19
+ import mlrun.api.schemas
19
20
  import mlrun.lists
20
21
  import mlrun.utils.helpers
21
22
 
22
- from .base import NotificationBase, NotificationSeverity
23
+ from .base import NotificationBase
23
24
 
24
25
 
25
26
  class ConsoleNotification(NotificationBase):
@@ -27,10 +28,12 @@ class ConsoleNotification(NotificationBase):
27
28
  Client only notification for printing run status notifications in console
28
29
  """
29
30
 
30
- def send(
31
+ def push(
31
32
  self,
32
33
  message: str,
33
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
34
+ severity: typing.Union[
35
+ mlrun.api.schemas.NotificationSeverity, str
36
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
34
37
  runs: typing.Union[mlrun.lists.RunList, list] = None,
35
38
  custom_html: str = None,
36
39
  ):
@@ -16,12 +16,13 @@ import json
16
16
  import os
17
17
  import typing
18
18
 
19
- import requests
19
+ import aiohttp
20
20
 
21
+ import mlrun.api.schemas
21
22
  import mlrun.errors
22
23
  import mlrun.lists
23
24
 
24
- from .base import NotificationBase, NotificationSeverity
25
+ from .base import NotificationBase
25
26
 
26
27
 
27
28
  class GitNotification(NotificationBase):
@@ -29,10 +30,12 @@ class GitNotification(NotificationBase):
29
30
  API/Client notification for setting a rich run statuses git issue comment (github/gitlab)
30
31
  """
31
32
 
32
- def send(
33
+ async def push(
33
34
  self,
34
35
  message: str,
35
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
36
+ severity: typing.Union[
37
+ mlrun.api.schemas.NotificationSeverity, str
38
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
36
39
  runs: typing.Union[mlrun.lists.RunList, list] = None,
37
40
  custom_html: str = None,
38
41
  ):
@@ -45,7 +48,7 @@ class GitNotification(NotificationBase):
45
48
  )
46
49
  server = self.params.get("server", None)
47
50
  gitlab = self.params.get("gitlab", False)
48
- self._pr_comment(
51
+ await self._pr_comment(
49
52
  self._get_html(message, severity, runs, custom_html),
50
53
  git_repo,
51
54
  git_issue,
@@ -55,7 +58,7 @@ class GitNotification(NotificationBase):
55
58
  )
56
59
 
57
60
  @staticmethod
58
- def _pr_comment(
61
+ async def _pr_comment(
59
62
  message: str,
60
63
  repo: str = None,
61
64
  issue: int = None,
@@ -111,9 +114,13 @@ class GitNotification(NotificationBase):
111
114
  "Authorization": f"token {token}",
112
115
  }
113
116
  url = f"https://{server}/repos/{repo}/issues/{issue}/comments"
114
- resp = requests.post(url=url, json={"body": str(message)}, headers=headers)
115
- if not resp.ok:
116
- raise mlrun.errors.MLRunBadRequestError(
117
- "Failed commenting on PR", response=resp.text, status=resp.status_code
118
- )
119
- return resp.json()["id"]
117
+
118
+ async with aiohttp.ClientSession() as session:
119
+ resp = await session.post(url, headers=headers, json={"body": message})
120
+ if not resp.ok:
121
+ resp_text = await resp.text()
122
+ raise mlrun.errors.MLRunBadRequestError(
123
+ f"Failed commenting on PR: {resp_text}", status=resp.status
124
+ )
125
+ data = await resp.json()
126
+ return data.get("id")
@@ -14,10 +14,11 @@
14
14
 
15
15
  import typing
16
16
 
17
+ import mlrun.api.schemas
17
18
  import mlrun.lists
18
19
  import mlrun.utils.helpers
19
20
 
20
- from .base import NotificationBase, NotificationSeverity
21
+ from .base import NotificationBase
21
22
 
22
23
 
23
24
  class IPythonNotification(NotificationBase):
@@ -45,10 +46,12 @@ class IPythonNotification(NotificationBase):
45
46
  def active(self) -> bool:
46
47
  return self._ipython is not None
47
48
 
48
- def send(
49
+ def push(
49
50
  self,
50
51
  message: str,
51
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
52
+ severity: typing.Union[
53
+ mlrun.api.schemas.NotificationSeverity, str
54
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
52
55
  runs: typing.Union[mlrun.lists.RunList, list] = None,
53
56
  custom_html: str = None,
54
57
  ):
@@ -12,15 +12,15 @@
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 typing
17
16
 
18
- import requests
17
+ import aiohttp
19
18
 
19
+ import mlrun.api.schemas
20
20
  import mlrun.lists
21
21
  import mlrun.utils.helpers
22
22
 
23
- from .base import NotificationBase, NotificationSeverity
23
+ from .base import NotificationBase
24
24
 
25
25
 
26
26
  class SlackNotification(NotificationBase):
@@ -34,10 +34,12 @@ class SlackNotification(NotificationBase):
34
34
  "error": ":x:",
35
35
  }
36
36
 
37
- def send(
37
+ async def push(
38
38
  self,
39
39
  message: str,
40
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
40
+ severity: typing.Union[
41
+ mlrun.api.schemas.NotificationSeverity, str
42
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
41
43
  runs: typing.Union[mlrun.lists.RunList, list] = None,
42
44
  custom_html: str = None,
43
45
  ):
@@ -53,17 +55,16 @@ class SlackNotification(NotificationBase):
53
55
 
54
56
  data = self._generate_slack_data(message, severity, runs)
55
57
 
56
- response = requests.post(
57
- webhook,
58
- data=json.dumps(data),
59
- headers={"Content-Type": "application/json"},
60
- )
61
- response.raise_for_status()
58
+ async with aiohttp.ClientSession() as session:
59
+ async with session.post(webhook, json=data) as response:
60
+ response.raise_for_status()
62
61
 
63
62
  def _generate_slack_data(
64
63
  self,
65
64
  message: str,
66
- severity: typing.Union[NotificationSeverity, str] = NotificationSeverity.INFO,
65
+ severity: typing.Union[
66
+ mlrun.api.schemas.NotificationSeverity, str
67
+ ] = mlrun.api.schemas.NotificationSeverity.INFO,
67
68
  runs: typing.Union[mlrun.lists.RunList, list] = None,
68
69
  ) -> dict:
69
70
  data = {