runnable 0.14.0__py3-none-any.whl → 0.17.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
runnable/utils.py CHANGED
@@ -21,7 +21,6 @@ from runnable import defaults, names
21
21
  from runnable.defaults import TypeMapVariable
22
22
 
23
23
  if TYPE_CHECKING: # pragma: no cover
24
- from extensions.nodes.nodes import TaskNode
25
24
  from runnable.nodes import BaseNode
26
25
 
27
26
 
@@ -428,25 +427,26 @@ def get_node_execution_command(
428
427
  log_level = log_level or logging.getLevelName(logger.getEffectiveLevel())
429
428
 
430
429
  action = (
431
- f"runnable execute_single_node {run_id} "
432
- f"{node._command_friendly_name()}"
433
- f" --log-level {log_level}"
430
+ f"runnable execute-single-node {run_id} "
431
+ f"{context.run_context.pipeline_file} "
432
+ f"{node._command_friendly_name()} "
433
+ f"--log-level {log_level} "
434
434
  )
435
435
 
436
- if context.run_context.pipeline_file:
437
- action = action + f" --file {context.run_context.pipeline_file}"
436
+ if context.run_context.from_sdk:
437
+ action = action + "--mode python "
438
438
 
439
439
  if map_variable:
440
- action = action + f" --map-variable '{json.dumps(map_variable)}'"
440
+ action = action + f"--map-variable '{json.dumps(map_variable)}' "
441
441
 
442
442
  if context.run_context.configuration_file:
443
- action = action + f" --config-file {context.run_context.configuration_file}"
443
+ action = action + f"--config {context.run_context.configuration_file} "
444
444
 
445
445
  if context.run_context.parameters_file:
446
- action = action + f" --parameters-file {context.run_context.parameters_file}"
446
+ action = action + f"--parameters-file {context.run_context.parameters_file} "
447
447
 
448
448
  if context.run_context.tag:
449
- action = action + f" --tag {context.run_context.tag}"
449
+ action = action + f"--tag {context.run_context.tag}"
450
450
 
451
451
  return action
452
452
 
@@ -476,8 +476,8 @@ def get_fan_command(
476
476
  action = (
477
477
  f"runnable fan {run_id} "
478
478
  f"{node._command_friendly_name()} "
479
+ f"{context.run_context.pipeline_file} "
479
480
  f"--mode {mode} "
480
- f"--file {context.run_context.pipeline_file} "
481
481
  f"--log-level {log_level} "
482
482
  )
483
483
  if context.run_context.configuration_file:
@@ -496,18 +496,10 @@ def get_fan_command(
496
496
 
497
497
 
498
498
  # TODO: This is not the right place for this.
499
- def get_job_execution_command(node: TaskNode, over_write_run_id: str = "") -> str:
499
+ def get_job_execution_command(over_write_run_id: str = "") -> str:
500
500
  """Get the execution command to run a job via command line.
501
501
 
502
502
  This function should be used by all executors to submit jobs in remote environment
503
-
504
- Args:
505
- executor (BaseExecutor): The executor class.
506
- node (BaseNode): The node being executed.
507
- over_write_run_id (str, optional): If the node is part of a map step. Defaults to ''.
508
-
509
- Returns:
510
- str: The execution command to run a job via command line.
511
503
  """
512
504
 
513
505
  run_id = context.run_context.run_id
@@ -517,24 +509,23 @@ def get_job_execution_command(node: TaskNode, over_write_run_id: str = "") -> st
517
509
 
518
510
  log_level = logging.getLevelName(logger.getEffectiveLevel())
519
511
 
520
- cli_command, cli_options = node.executable.get_cli_options()
521
-
522
- action = f"runnable execute_{cli_command} {run_id} " f" --log-level {log_level}"
523
-
524
- action = action + f" --entrypoint {defaults.ENTRYPOINT.SYSTEM.value}"
512
+ action = (
513
+ f"runnable execute-job /app/{context.run_context.job_definition_file} {run_id} "
514
+ f" --log-level {log_level}"
515
+ )
525
516
 
526
517
  if context.run_context.configuration_file:
527
- action = action + f" --config-file {context.run_context.configuration_file}"
518
+ action = action + f" --config {context.run_context.configuration_file}"
528
519
 
529
520
  if context.run_context.parameters_file:
530
- action = action + f" --parameters-file {context.run_context.parameters_file}"
521
+ action = action + f" --parameters {context.run_context.parameters_file}"
522
+
523
+ if context.run_context.from_sdk:
524
+ action = action + " --mode python "
531
525
 
532
526
  if context.run_context.tag:
533
527
  action = action + f" --tag {context.run_context.tag}"
534
528
 
535
- for key, value in cli_options.items():
536
- action = action + f" --{key} {value}"
537
-
538
529
  return action
539
530
 
540
531
 
@@ -555,6 +546,7 @@ def get_provider_by_name_and_type(
555
546
  Returns:
556
547
  object: A service object
557
548
  """
549
+
558
550
  namespace = service_type
559
551
 
560
552
  service_name = service_details["type"]
@@ -1,16 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runnable
3
- Version: 0.14.0
3
+ Version: 0.17.0
4
4
  Summary: Add your description here
5
5
  Author-email: "Vammi, Vijay" <vijay.vammi@astrazeneca.com>
6
6
  License-File: LICENSE
7
- Requires-Python: >=3.9
7
+ Requires-Python: >=3.10
8
8
  Requires-Dist: catalog
9
9
  Requires-Dist: click-plugins>=1.1.1
10
10
  Requires-Dist: click<=8.1.3
11
11
  Requires-Dist: dill>=0.3.9
12
- Requires-Dist: executor
12
+ Requires-Dist: job-executor
13
13
  Requires-Dist: nodes
14
+ Requires-Dist: pipeline-executor
14
15
  Requires-Dist: pydantic>=2.10.3
15
16
  Requires-Dist: python-dotenv>=1.0.1
16
17
  Requires-Dist: rich>=13.9.4
@@ -24,6 +25,8 @@ Provides-Extra: docker
24
25
  Requires-Dist: docker>=7.1.0; extra == 'docker'
25
26
  Provides-Extra: examples
26
27
  Requires-Dist: pandas>=2.2.3; extra == 'examples'
28
+ Provides-Extra: k8s
29
+ Requires-Dist: kubernetes>=31.0.0; extra == 'k8s'
27
30
  Provides-Extra: notebook
28
31
  Requires-Dist: ploomber-engine>=0.0.33; extra == 'notebook'
29
32
  Description-Content-Type: text/markdown
@@ -0,0 +1,23 @@
1
+ runnable/__init__.py,sha256=KqpLDTD1CfdEj2aDyEkSn2KW-_83qyrRrrWLc5lZVM4,624
2
+ runnable/catalog.py,sha256=MiEmb-18liAKmgeMdDF41VVn0ZEAVLP8hR33oacQ1zs,4930
3
+ runnable/cli.py,sha256=01zmzOdynEmLI4vWDtSHQ6y1od_Jlc8G1RF69fi2L8g,8446
4
+ runnable/context.py,sha256=by5uepmuCP0dmM9BmsliXihSes5QEFejwAsmekcqylE,1388
5
+ runnable/datastore.py,sha256=9y5enzn6AXLHLdwvgkdjGPrBkVlrcjfbaAHsst-lJzg,32466
6
+ runnable/defaults.py,sha256=3o9IVGryyCE6PoQTOoaIaHHTbJGEzmdXMcwzOhwAYoI,3518
7
+ runnable/entrypoints.py,sha256=67gPBiIIS4Kd9g6LdoGCraRJPda8K1i7Lp7XcD2iY5k,18913
8
+ runnable/exceptions.py,sha256=LFbp0-Qxg2PAMLEVt7w2whhBxSG-5pzUEv5qN-Rc4_c,3003
9
+ runnable/executor.py,sha256=Rafu9EECrNq1LBkJmS6KYCekchP5ufrR04mHWG-JzqQ,15543
10
+ runnable/graph.py,sha256=jVjikRLR-so3b2ufmNKpEQ_Ny68qN4bcGDAdXBRKiCY,16574
11
+ runnable/names.py,sha256=vn92Kv9ANROYSZX6Z4z1v_WA3WiEdIYmG6KEStBFZug,8134
12
+ runnable/nodes.py,sha256=YU9u7r1ESzui1uVtJ1dgwdv1ozyJnF2k-MCFieT8CLI,17519
13
+ runnable/parameters.py,sha256=g_bJurLjuppFDiDpfFqy6BRF36o_EY0OC5APl7HJFok,5450
14
+ runnable/pickler.py,sha256=ydJ_eti_U1F4l-YacFp7BWm6g5vTn04UXye25S1HVok,2684
15
+ runnable/sdk.py,sha256=hwdk2dLmJsOTs2GnOlayw8WfliyeZFpA6Tcnp3tgblg,33370
16
+ runnable/secrets.py,sha256=PXcEJw-4WPzeWRLfsatcPPyr1zkqgHzdRWRcS9vvpvM,2354
17
+ runnable/tasks.py,sha256=JnIIYQf3YUidHXIN6hiUIfDnegc7_rJMNXuHW4WS9ig,29378
18
+ runnable/utils.py,sha256=wqyN7lMW56cBqyE59iDE6_i2HXPkvEUCQ-66UQnIwTA,19993
19
+ runnable-0.17.0.dist-info/METADATA,sha256=aPu6b9A_JP90oLpCFcnSJStDaizL5-sXbahPvjWU5Wo,10102
20
+ runnable-0.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ runnable-0.17.0.dist-info/entry_points.txt,sha256=I92DYldRrCb9HCsoum8GjC2UsQrWpuw2kawXTZpkIz4,1559
22
+ runnable-0.17.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
23
+ runnable-0.17.0.dist-info/RECORD,,
@@ -1,16 +1,14 @@
1
1
  [console_scripts]
2
- runnable = runnable.cli:cli
2
+ runnable = runnable.cli:app
3
3
 
4
4
  [catalog]
5
5
  do-nothing = runnable.catalog:DoNothingCatalog
6
6
  file-system = extensions.catalog.file_system:FileSystemCatalog
7
7
 
8
- [executor]
9
- argo = extensions.executor.argo:ArgoExecutor
10
- local = extensions.executor.local:LocalExecutor
11
- local-container = extensions.executor.local_container:LocalContainerExecutor
12
- mocked = extensions.executor.mocked:MockedExecutor
13
- retry = extensions.executor.retry:RetryExecutor
8
+ [job_executor]
9
+ k8s-job = extensions.job_executor.k8s:K8sJobExecutor
10
+ local = extensions.job_executor.local:LocalJobExecutor
11
+ local-container = extensions.job_executor.local_container:LocalContainerJobExecutor
14
12
 
15
13
  [nodes]
16
14
  dag = extensions.nodes.nodes:DagNode
@@ -24,6 +22,13 @@ task = extensions.nodes.nodes:TaskNode
24
22
  [pickler]
25
23
  pickle = runnable.pickler:NativePickler
26
24
 
25
+ [pipeline_executor]
26
+ argo = extensions.pipeline_executor.argo:ArgoExecutor
27
+ local = extensions.pipeline_executor.local:LocalExecutor
28
+ local-container = extensions.pipeline_executor.local_container:LocalContainerExecutor
29
+ mocked = extensions.pipeline_executor.mocked:MockedExecutor
30
+ retry = extensions.pipeline_executor.retry:RetryExecutor
31
+
27
32
  [run_log_store]
28
33
  buffered = runnable.datastore:BufferRunLogstore
29
34
  chunked-fs = extensions.run_log_store.chunked_fs:ChunkedFileSystemRunLogStore
runnable/integration.py DELETED
@@ -1,197 +0,0 @@
1
- import logging
2
-
3
- from stevedore import extension
4
-
5
- from runnable import defaults
6
- from runnable.executor import BaseExecutor
7
-
8
- logger = logging.getLogger(defaults.LOGGER_NAME)
9
- logging.getLogger("stevedore").setLevel(logging.CRITICAL)
10
-
11
- # --8<-- [start:docs]
12
-
13
-
14
- class BaseIntegration:
15
- """
16
- Base class for handling integration between Executor and one of Catalog, Secrets, RunLogStore.
17
- """
18
-
19
- executor_type = ""
20
- service_type = "" # One of secret, catalog, datastore, experiment tracker
21
- service_provider = "" # The actual implementation of the service
22
-
23
- def __init__(self, executor: "BaseExecutor", integration_service: object):
24
- self.executor = executor
25
- self.service = integration_service
26
-
27
- def validate(self, **kwargs):
28
- """
29
- Raise an exception if the executor_type is not compatible with service provider.
30
-
31
- By default, it is considered as compatible.
32
- """
33
-
34
- def configure_for_traversal(self, **kwargs):
35
- """
36
- Do any changes needed to both executor and service provider during traversal of the graph.
37
-
38
- By default, no change is required.
39
- """
40
-
41
- def configure_for_execution(self, **kwargs):
42
- """
43
- Do any changes needed to both executor and service provider during execution of a node.
44
-
45
- By default, no change is required.
46
- """
47
-
48
-
49
- # --8<-- [end:docs]
50
-
51
-
52
- def get_integration_handler(
53
- executor: "BaseExecutor", service: object
54
- ) -> BaseIntegration:
55
- """
56
- Return the integration handler between executor and the service.
57
-
58
- If none found to be implemented, return the BaseIntegration which does nothing.
59
-
60
- Args:
61
- executor (BaseExecutor): The executor
62
- service (object): The service provider
63
-
64
- Returns:
65
- [BaseIntegration]: The implemented integration handler or BaseIntegration if none found
66
-
67
- Raises:
68
- Exception: If multiple integrations are found for the executor and service
69
- """
70
- service_type = service.service_type # type: ignore
71
- service_name = getattr(service, "service_name")
72
- integrations = []
73
-
74
- # Get all the integrations defined by the 3rd party in their pyproject.toml
75
- mgr = extension.ExtensionManager(
76
- namespace="integration",
77
- invoke_on_load=True,
78
- invoke_kwds={"executor": executor, "integration_service": service},
79
- )
80
- for _, kls in mgr.items():
81
- if (
82
- kls.obj.executor_type == executor.service_name
83
- and kls.obj.service_type == service_type
84
- and kls.obj.service_provider == service_name
85
- ):
86
- logger.info(f"Identified an integration pattern {kls.obj}")
87
- integrations.append(kls.obj)
88
-
89
- # Get all the implementations defined by the runnable package
90
- for kls in BaseIntegration.__subclasses__():
91
- # Match the exact service type
92
- if kls.service_type == service_type and kls.service_provider == service_name:
93
- # Match either all executor or specific ones provided
94
- if kls.executor_type == "" or kls.executor_type == executor.service_name:
95
- integrations.append(kls(executor=executor, integration_service=service))
96
-
97
- if len(integrations) > 1:
98
- msg = (
99
- f"Multiple integrations between {executor.service_name} and {service_name} of type {service_type} found. "
100
- "If you defined an integration pattern, please ensure it is specific and does not conflict with runnable "
101
- " implementations."
102
- )
103
- logger.exception(msg)
104
- raise Exception(msg)
105
-
106
- if not integrations:
107
- logger.info(
108
- f"Could not find an integration pattern for {executor.service_name} and {service_name} for {service_type}."
109
- " This implies that there is no need to change the configurations."
110
- )
111
- return BaseIntegration(executor, service)
112
-
113
- return integrations[0]
114
-
115
-
116
- def validate(executor: "BaseExecutor", service: object, **kwargs):
117
- """
118
- Helper function to resolve the Integration class and validate the compatibility between executor and service
119
-
120
- Args:
121
- executor (BaseExecutor) : The executor
122
- service (object): The service provider
123
- """
124
- integration_handler = get_integration_handler(executor, service)
125
- integration_handler.validate(**kwargs)
126
-
127
-
128
- def configure_for_traversal(executor: "BaseExecutor", service: object, **kwargs):
129
- """
130
- Helper function to resolve the Integration class and configure the executor and service for graph traversal
131
-
132
- Args:
133
- executor (BaseExecutor) : The executor
134
- service (object): The service provider
135
- """
136
- integration_handler = get_integration_handler(executor, service)
137
- integration_handler.configure_for_traversal(**kwargs)
138
-
139
-
140
- def configure_for_execution(executor: "BaseExecutor", service: object, **kwargs):
141
- """
142
- Helper function to resolve the Integration class and configure the executor and service for execution
143
-
144
- Args:
145
- executor (BaseExecutor) : The executor
146
- service (object): The service provider
147
- """
148
- integration_handler = get_integration_handler(executor, service)
149
- integration_handler.configure_for_execution(**kwargs)
150
-
151
-
152
- # TODO: Move all of these to the proper locations
153
- class BufferedRunLogStore(BaseIntegration):
154
- """
155
- Integration between any executor and buffered run log store
156
- """
157
-
158
- service_type = "run_log_store" # One of secret, catalog, datastore
159
- service_provider = "buffered" # The actual implementation of the service
160
-
161
- def validate(self, **kwargs):
162
- if not self.executor.service_name == "local":
163
- raise Exception(
164
- "Buffered run log store is only supported for local executor"
165
- )
166
-
167
- msg = (
168
- "Run log generated by buffered run log store are not persisted. "
169
- "Re-running this run, in case of a failure, is not possible"
170
- )
171
- logger.info(msg)
172
-
173
-
174
- class DoNothingCatalog(BaseIntegration):
175
- """
176
- Integration between any executor and do nothing catalog
177
- """
178
-
179
- service_type = "catalog" # One of secret, catalog, datastore
180
- service_provider = "do-nothing" # The actual implementation of the service
181
-
182
- def validate(self, **kwargs):
183
- msg = "A do-nothing catalog does not hold any data and therefore cannot pass data between nodes."
184
- logger.info(msg)
185
-
186
-
187
- class DoNothingSecrets(BaseIntegration):
188
- """
189
- Integration between any executor and do nothing secrets
190
- """
191
-
192
- service_type = "secrets" # One of secret, catalog, datastore
193
- service_provider = "do-nothing" # The actual implementation of the service
194
-
195
- def validate(self, **kwargs):
196
- msg = "A do-nothing secrets does not hold any secrets and therefore cannot return you any secrets."
197
- logger.info(msg)
@@ -1,24 +0,0 @@
1
- runnable/__init__.py,sha256=WuJwXEBxjiz2E1jBapkOkYpIaCAPZ1Udyep0dnN4bkE,666
2
- runnable/catalog.py,sha256=5eTYwZWqfVBXIIn8WbweTMqiXZ9ccvtJBnAiIxSQ3Vk,4835
3
- runnable/cli.py,sha256=rBTvkNDetN6psHmDLa0kko8IHGvND5xMuO30hU_gcvY,9931
4
- runnable/context.py,sha256=QhiXJHRcEBfSKB1ijvL5yB9w44x0HCe7VEiwK1cUJ9U,1124
5
- runnable/datastore.py,sha256=Q_KKb4PNP2IXnUlR2bjOclDFAsAVJ_oNiCd5x0vB5jc,28127
6
- runnable/defaults.py,sha256=HYkXNI2hg0Y-SsXySjliwdc-3FUGJvJV3TnarmMIFFs,4656
7
- runnable/entrypoints.py,sha256=gMywHyoUheSAXCyqLMJ0QWK4IxiFVgEYwRDuZWsk-uI,18612
8
- runnable/exceptions.py,sha256=3gyN2bhqYvaZF_bo8hA7I09u8aQCAeh8NclBp5lCH8w,2574
9
- runnable/executor.py,sha256=Y-yCw4ZIz88nHn47QzCXvXm7VjByTIyBWzsqsaIpNP8,14653
10
- runnable/graph.py,sha256=EuH0210DcbEFlc6J-aSvfXJOb0SqORUiTpgFYyb_KPM,16602
11
- runnable/integration.py,sha256=IXBH20QKpFYW7pQwwbTI0qQvrg4kJseM0KMacQKli74,6791
12
- runnable/names.py,sha256=vn92Kv9ANROYSZX6Z4z1v_WA3WiEdIYmG6KEStBFZug,8134
13
- runnable/nodes.py,sha256=I9C65nj3kAHHXJSwn5QYximFjV7tbjBiTk0ayEgrmK4,16526
14
- runnable/parameters.py,sha256=g_bJurLjuppFDiDpfFqy6BRF36o_EY0OC5APl7HJFok,5450
15
- runnable/pickler.py,sha256=ydJ_eti_U1F4l-YacFp7BWm6g5vTn04UXye25S1HVok,2684
16
- runnable/sdk.py,sha256=tEwTwcfm1KVfnEql3G_yJpgymDWOqoIIA4q3RzKmHp0,30365
17
- runnable/secrets.py,sha256=PXcEJw-4WPzeWRLfsatcPPyr1zkqgHzdRWRcS9vvpvM,2354
18
- runnable/tasks.py,sha256=QPCgH_D7YkN2oAi7-w6Ipt9IZ397SayjhAl_PPyVto8,29822
19
- runnable/utils.py,sha256=THMHnWVrUhNKdIvUbeZdDiXnP1WEOuee9e9OB8zzW5M,20441
20
- runnable-0.14.0.dist-info/METADATA,sha256=n3mrLZadanBuHaUz4h5WgtsfuXmkjaHdtsfOCSAmEEk,9994
21
- runnable-0.14.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
22
- runnable-0.14.0.dist-info/entry_points.txt,sha256=8yBeduXOnO3SUnafZQwzXiE8rQMPXGDbqueyL7G9euM,1297
23
- runnable-0.14.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
24
- runnable-0.14.0.dist-info/RECORD,,