runnable 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runnable/__init__.py +34 -0
- runnable/catalog.py +141 -0
- runnable/cli.py +272 -0
- runnable/context.py +34 -0
- runnable/datastore.py +686 -0
- runnable/defaults.py +179 -0
- runnable/entrypoints.py +484 -0
- runnable/exceptions.py +94 -0
- runnable/executor.py +431 -0
- runnable/experiment_tracker.py +139 -0
- runnable/extensions/catalog/__init__.py +21 -0
- runnable/extensions/catalog/file_system/__init__.py +0 -0
- runnable/extensions/catalog/file_system/implementation.py +226 -0
- runnable/extensions/catalog/k8s_pvc/__init__.py +0 -0
- runnable/extensions/catalog/k8s_pvc/implementation.py +16 -0
- runnable/extensions/catalog/k8s_pvc/integration.py +59 -0
- runnable/extensions/executor/__init__.py +714 -0
- runnable/extensions/executor/argo/__init__.py +0 -0
- runnable/extensions/executor/argo/implementation.py +1182 -0
- runnable/extensions/executor/argo/specification.yaml +51 -0
- runnable/extensions/executor/k8s_job/__init__.py +0 -0
- runnable/extensions/executor/k8s_job/implementation_FF.py +259 -0
- runnable/extensions/executor/k8s_job/integration_FF.py +69 -0
- runnable/extensions/executor/local/__init__.py +0 -0
- runnable/extensions/executor/local/implementation.py +69 -0
- runnable/extensions/executor/local_container/__init__.py +0 -0
- runnable/extensions/executor/local_container/implementation.py +367 -0
- runnable/extensions/executor/mocked/__init__.py +0 -0
- runnable/extensions/executor/mocked/implementation.py +220 -0
- runnable/extensions/experiment_tracker/__init__.py +0 -0
- runnable/extensions/experiment_tracker/mlflow/__init__.py +0 -0
- runnable/extensions/experiment_tracker/mlflow/implementation.py +94 -0
- runnable/extensions/nodes.py +675 -0
- runnable/extensions/run_log_store/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_file_system/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_file_system/implementation.py +106 -0
- runnable/extensions/run_log_store/chunked_k8s_pvc/__init__.py +0 -0
- runnable/extensions/run_log_store/chunked_k8s_pvc/implementation.py +21 -0
- runnable/extensions/run_log_store/chunked_k8s_pvc/integration.py +61 -0
- runnable/extensions/run_log_store/db/implementation_FF.py +157 -0
- runnable/extensions/run_log_store/db/integration_FF.py +0 -0
- runnable/extensions/run_log_store/file_system/__init__.py +0 -0
- runnable/extensions/run_log_store/file_system/implementation.py +136 -0
- runnable/extensions/run_log_store/generic_chunked.py +541 -0
- runnable/extensions/run_log_store/k8s_pvc/__init__.py +0 -0
- runnable/extensions/run_log_store/k8s_pvc/implementation.py +21 -0
- runnable/extensions/run_log_store/k8s_pvc/integration.py +56 -0
- runnable/extensions/secrets/__init__.py +0 -0
- runnable/extensions/secrets/dotenv/__init__.py +0 -0
- runnable/extensions/secrets/dotenv/implementation.py +100 -0
- runnable/extensions/secrets/env_secrets/__init__.py +0 -0
- runnable/extensions/secrets/env_secrets/implementation.py +42 -0
- runnable/graph.py +464 -0
- runnable/integration.py +205 -0
- runnable/interaction.py +399 -0
- runnable/names.py +546 -0
- runnable/nodes.py +489 -0
- runnable/parameters.py +183 -0
- runnable/pickler.py +102 -0
- runnable/sdk.py +470 -0
- runnable/secrets.py +95 -0
- runnable/tasks.py +392 -0
- runnable/utils.py +630 -0
- runnable-0.2.0.dist-info/METADATA +437 -0
- runnable-0.2.0.dist-info/RECORD +69 -0
- runnable-0.2.0.dist-info/entry_points.txt +44 -0
- runnable-0.1.0.dist-info/METADATA +0 -16
- runnable-0.1.0.dist-info/RECORD +0 -6
- /runnable/{.gitkeep → extensions/__init__.py} +0 -0
- {runnable-0.1.0.dist-info → runnable-0.2.0.dist-info}/LICENSE +0 -0
- {runnable-0.1.0.dist-info → runnable-0.2.0.dist-info}/WHEEL +0 -0
runnable/defaults.py
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
# mypy: ignore-errors
|
2
|
+
# The above should be done until https://github.com/python/mypy/issues/8823
|
3
|
+
from enum import Enum
|
4
|
+
from typing import Any, Dict, Mapping, Optional, Union
|
5
|
+
|
6
|
+
from typing_extensions import TypeAlias
|
7
|
+
|
8
|
+
# TODO: This is not the correct way to do this.
|
9
|
+
try: # pragma: no cover
|
10
|
+
from typing import TypedDict # type: ignore[unused-ignore]
|
11
|
+
except ImportError: # pragma: no cover
|
12
|
+
from typing_extensions import TypedDict # type: ignore[unused-ignore]
|
13
|
+
|
14
|
+
|
15
|
+
NAME = "magnus"
|
16
|
+
LOGGER_NAME = "magnus"
|
17
|
+
|
18
|
+
# CLI settings
|
19
|
+
LOG_LEVEL = "WARNING"
|
20
|
+
|
21
|
+
|
22
|
+
class EXECUTION_PLAN(Enum):
|
23
|
+
"""
|
24
|
+
The possible execution plans for a magnus job.
|
25
|
+
"""
|
26
|
+
|
27
|
+
CHAINED = "chained" # 121 relationship between run log and the dag.
|
28
|
+
UNCHAINED = "unchained" # Only captures execution of steps, no relation.
|
29
|
+
INTERACTIVE = "interactive" # used for interactive sessions
|
30
|
+
|
31
|
+
|
32
|
+
# Type definitions
|
33
|
+
class ServiceConfig(TypedDict):
|
34
|
+
type: str
|
35
|
+
config: Mapping[str, Any]
|
36
|
+
|
37
|
+
|
38
|
+
class MagnusConfig(TypedDict, total=False):
|
39
|
+
run_log_store: Optional[ServiceConfig]
|
40
|
+
secrets: Optional[ServiceConfig]
|
41
|
+
catalog: Optional[ServiceConfig]
|
42
|
+
executor: Optional[ServiceConfig]
|
43
|
+
experiment_tracker: Optional[ServiceConfig]
|
44
|
+
|
45
|
+
|
46
|
+
TypeMapVariable: TypeAlias = Optional[Dict[str, Union[str, int, float]]]
|
47
|
+
|
48
|
+
|
49
|
+
# Config file environment variable
|
50
|
+
MAGNUS_CONFIG_FILE = "MAGNUS_CONFIG_FILE"
|
51
|
+
MAGNUS_RUN_TAG = "MAGNUS_RUN_TAG"
|
52
|
+
|
53
|
+
# Interaction settings
|
54
|
+
TRACK_PREFIX = "MAGNUS_TRACK_"
|
55
|
+
STEP_INDICATOR = "_STEP_"
|
56
|
+
PARAMETER_PREFIX = "MAGNUS_PRM_"
|
57
|
+
MAP_VARIABLE = "MAGNUS_MAP_VARIABLE"
|
58
|
+
VARIABLE_PREFIX = "MAGNUS_VAR_"
|
59
|
+
ENV_RUN_ID = "MAGNUS_RUN_ID"
|
60
|
+
ATTEMPT_NUMBER = "MAGNUS_STEP_ATTEMPT"
|
61
|
+
|
62
|
+
# STATUS progression
|
63
|
+
# For Branch, CREATED -> PROCESSING -> SUCCESS OR FAIL
|
64
|
+
# For a step, CREATED -> TRIGGERED -> PROCESSING -> SUCCESS OR FAIL
|
65
|
+
CREATED = "CREATED"
|
66
|
+
PROCESSING = "PROCESSING"
|
67
|
+
SUCCESS = "SUCCESS"
|
68
|
+
FAIL = "FAIL"
|
69
|
+
TRIGGERED = "TRIGGERED"
|
70
|
+
|
71
|
+
# Node and Command settings
|
72
|
+
COMMAND_TYPE = "python"
|
73
|
+
NODE_SPEC_FILE = "node_spec.yaml"
|
74
|
+
COMMAND_FRIENDLY_CHARACTER = "%"
|
75
|
+
DEFAULT_CONTAINER_CONTEXT_PATH = "/opt/magnus/"
|
76
|
+
DEFAULT_CONTAINER_DATA_PATH = "data/"
|
77
|
+
DEFAULT_CONTAINER_OUTPUT_PARAMETERS = "parameters.json"
|
78
|
+
|
79
|
+
# Default services
|
80
|
+
DEFAULT_EXECUTOR = ServiceConfig(type="local", config={})
|
81
|
+
DEFAULT_RUN_LOG_STORE = ServiceConfig(type="buffered", config={})
|
82
|
+
DEFAULT_CATALOG = ServiceConfig(type="file-system", config={})
|
83
|
+
DEFAULT_SECRETS = ServiceConfig(type="do-nothing", config={})
|
84
|
+
DEFAULT_EXPERIMENT_TRACKER = ServiceConfig(type="do-nothing", config={})
|
85
|
+
|
86
|
+
# Map state
|
87
|
+
MAP_PLACEHOLDER = "map_variable_placeholder"
|
88
|
+
|
89
|
+
# Dag node
|
90
|
+
DAG_BRANCH_NAME = "dag"
|
91
|
+
|
92
|
+
# RUN settings
|
93
|
+
RANDOM_RUN_ID_LEN = 6
|
94
|
+
MAX_TIME = 86400 # 1 day in seconds
|
95
|
+
|
96
|
+
# User extensions
|
97
|
+
USER_CONFIG_FILE = "magnus-config.yaml"
|
98
|
+
|
99
|
+
# Executor settings
|
100
|
+
ENABLE_PARALLEL = False
|
101
|
+
|
102
|
+
# RUN log store settings
|
103
|
+
LOG_LOCATION_FOLDER = ".run_log_store"
|
104
|
+
|
105
|
+
# Dag node
|
106
|
+
DAG_BRANCH_NAME = "dag"
|
107
|
+
|
108
|
+
# Data catalog settings
|
109
|
+
CATALOG_LOCATION_FOLDER = ".catalog"
|
110
|
+
COMPUTE_DATA_FOLDER = "."
|
111
|
+
|
112
|
+
# Secrets settings
|
113
|
+
DOTENV_FILE_LOCATION = ".env"
|
114
|
+
|
115
|
+
|
116
|
+
# Docker settings
|
117
|
+
DOCKERFILE_NAME = "Dockerfile"
|
118
|
+
DOCKERFILE_CONTENT = r"""# Python 3.8 Image without Dependecies
|
119
|
+
FROM python:3.8
|
120
|
+
|
121
|
+
LABEL maintainer="mesanthu@gmail.com"
|
122
|
+
|
123
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
124
|
+
git \
|
125
|
+
&& rm -rf /var/lib/apt/lists/*
|
126
|
+
|
127
|
+
${INSTALL_STYLE}
|
128
|
+
|
129
|
+
ENV VIRTUAL_ENV=/opt/venv
|
130
|
+
RUN python -m virtualenv --python=/usr/local/bin/python $VIRTUAL_ENV
|
131
|
+
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
132
|
+
|
133
|
+
${COPY_CONTENT}
|
134
|
+
WORKDIR /app
|
135
|
+
|
136
|
+
${INSTALL_REQUIREMENTS}
|
137
|
+
"""
|
138
|
+
GIT_ARCHIVE_NAME = "git_tracked"
|
139
|
+
LEN_SHA_FOR_TAG = 8
|
140
|
+
|
141
|
+
|
142
|
+
class ENTRYPOINT(Enum):
|
143
|
+
"""
|
144
|
+
The possible container entrypoint types.
|
145
|
+
"""
|
146
|
+
|
147
|
+
USER = "user"
|
148
|
+
SYSTEM = "system"
|
149
|
+
|
150
|
+
|
151
|
+
## Logging settings
|
152
|
+
|
153
|
+
LOGGING_CONFIG = {
|
154
|
+
"version": 1,
|
155
|
+
"disable_existing_loggers": True,
|
156
|
+
"formatters": {
|
157
|
+
"standard": {"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"},
|
158
|
+
"magnus_formatter": {"format": "%(message)s", "datefmt": "[%X]"},
|
159
|
+
},
|
160
|
+
"handlers": {
|
161
|
+
"default": {
|
162
|
+
"formatter": "standard",
|
163
|
+
"class": "logging.StreamHandler",
|
164
|
+
"stream": "ext://sys.stdout", # Default is stderr
|
165
|
+
},
|
166
|
+
"magnus_handler": {
|
167
|
+
"formatter": "magnus_formatter",
|
168
|
+
"class": "rich.logging.RichHandler",
|
169
|
+
"rich_tracebacks": True,
|
170
|
+
},
|
171
|
+
},
|
172
|
+
"loggers": {
|
173
|
+
"": {
|
174
|
+
"handlers": ["default"],
|
175
|
+
"propagate": True,
|
176
|
+
}, # Root logger
|
177
|
+
LOGGER_NAME: {"handlers": ["magnus_handler"], "propagate": False},
|
178
|
+
},
|
179
|
+
}
|
runnable/entrypoints.py
ADDED
@@ -0,0 +1,484 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
from typing import Optional, cast
|
4
|
+
|
5
|
+
from rich import print
|
6
|
+
|
7
|
+
import runnable.context as context
|
8
|
+
from runnable import defaults, graph, utils
|
9
|
+
from runnable.defaults import MagnusConfig, ServiceConfig
|
10
|
+
|
11
|
+
logger = logging.getLogger(defaults.LOGGER_NAME)
|
12
|
+
|
13
|
+
|
14
|
+
def get_default_configs() -> MagnusConfig:
|
15
|
+
"""
|
16
|
+
User can provide extensions as part of their code base, magnus-config.yaml provides the place to put them.
|
17
|
+
"""
|
18
|
+
user_configs = {}
|
19
|
+
if utils.does_file_exist(defaults.USER_CONFIG_FILE):
|
20
|
+
user_configs = utils.load_yaml(defaults.USER_CONFIG_FILE)
|
21
|
+
|
22
|
+
if not user_configs:
|
23
|
+
return {}
|
24
|
+
|
25
|
+
user_defaults = user_configs.get("defaults", {})
|
26
|
+
if user_defaults:
|
27
|
+
return user_defaults
|
28
|
+
|
29
|
+
return {}
|
30
|
+
|
31
|
+
|
32
|
+
def prepare_configurations(
|
33
|
+
run_id: str,
|
34
|
+
configuration_file: str = "",
|
35
|
+
pipeline_file: str = "",
|
36
|
+
tag: str = "",
|
37
|
+
use_cached: str = "",
|
38
|
+
parameters_file: str = "",
|
39
|
+
force_local_executor: bool = False,
|
40
|
+
) -> context.Context:
|
41
|
+
"""
|
42
|
+
Replace the placeholders in the dag/config against the variables file.
|
43
|
+
|
44
|
+
Attach the secrets_handler, run_log_store, catalog_handler to the executor and return it.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
variables_file (str): The variables file, if used or None
|
48
|
+
pipeline_file (str): The config/dag file
|
49
|
+
run_id (str): The run id of the run.
|
50
|
+
tag (str): If a tag is provided at the run time
|
51
|
+
use_cached (str): Provide the run_id of the older run
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
executor.BaseExecutor : A prepared executor as per the dag/config
|
55
|
+
"""
|
56
|
+
magnus_defaults = get_default_configs()
|
57
|
+
|
58
|
+
variables = utils.gather_variables()
|
59
|
+
|
60
|
+
templated_configuration = {}
|
61
|
+
if configuration_file:
|
62
|
+
templated_configuration = utils.load_yaml(configuration_file) or {}
|
63
|
+
|
64
|
+
configuration: MagnusConfig = cast(MagnusConfig, templated_configuration)
|
65
|
+
|
66
|
+
# Run log settings, configuration over-rides everything
|
67
|
+
run_log_config: Optional[ServiceConfig] = configuration.get("run_log_store", None)
|
68
|
+
if not run_log_config:
|
69
|
+
run_log_config = cast(ServiceConfig, magnus_defaults.get("run_log_store", defaults.DEFAULT_RUN_LOG_STORE))
|
70
|
+
run_log_store = utils.get_provider_by_name_and_type("run_log_store", run_log_config)
|
71
|
+
|
72
|
+
# Catalog handler settings, configuration over-rides everything
|
73
|
+
catalog_config: Optional[ServiceConfig] = configuration.get("catalog", None)
|
74
|
+
if not catalog_config:
|
75
|
+
catalog_config = cast(ServiceConfig, magnus_defaults.get("catalog", defaults.DEFAULT_CATALOG))
|
76
|
+
catalog_handler = utils.get_provider_by_name_and_type("catalog", catalog_config)
|
77
|
+
|
78
|
+
# Secret handler settings, configuration over-rides everything
|
79
|
+
secrets_config: Optional[ServiceConfig] = configuration.get("secrets", None)
|
80
|
+
if not secrets_config:
|
81
|
+
secrets_config = cast(ServiceConfig, magnus_defaults.get("secrets", defaults.DEFAULT_SECRETS))
|
82
|
+
secrets_handler = utils.get_provider_by_name_and_type("secrets", secrets_config)
|
83
|
+
|
84
|
+
# experiment tracker settings, configuration over-rides everything
|
85
|
+
tracker_config: Optional[ServiceConfig] = configuration.get("experiment_tracker", None)
|
86
|
+
if not tracker_config:
|
87
|
+
tracker_config = cast(
|
88
|
+
ServiceConfig, magnus_defaults.get("experiment_tracker", defaults.DEFAULT_EXPERIMENT_TRACKER)
|
89
|
+
)
|
90
|
+
tracker_handler = utils.get_provider_by_name_and_type("experiment_tracker", tracker_config)
|
91
|
+
|
92
|
+
# executor configurations, configuration over rides everything
|
93
|
+
executor_config: Optional[ServiceConfig] = configuration.get("executor", None)
|
94
|
+
if force_local_executor:
|
95
|
+
executor_config = ServiceConfig(type="local", config={})
|
96
|
+
|
97
|
+
if not executor_config:
|
98
|
+
executor_config = cast(ServiceConfig, magnus_defaults.get("executor", defaults.DEFAULT_EXECUTOR))
|
99
|
+
configured_executor = utils.get_provider_by_name_and_type("executor", executor_config)
|
100
|
+
|
101
|
+
# Construct the context
|
102
|
+
run_context = context.Context(
|
103
|
+
executor=configured_executor,
|
104
|
+
run_log_store=run_log_store,
|
105
|
+
catalog_handler=catalog_handler,
|
106
|
+
secrets_handler=secrets_handler,
|
107
|
+
experiment_tracker=tracker_handler,
|
108
|
+
variables=variables,
|
109
|
+
tag=tag,
|
110
|
+
run_id=run_id,
|
111
|
+
configuration_file=configuration_file,
|
112
|
+
parameters_file=parameters_file,
|
113
|
+
)
|
114
|
+
|
115
|
+
if pipeline_file:
|
116
|
+
# There are use cases where we are only preparing the executor
|
117
|
+
pipeline_config = utils.load_yaml(pipeline_file)
|
118
|
+
|
119
|
+
logger.info("The input pipeline:")
|
120
|
+
logger.info(json.dumps(pipeline_config, indent=4))
|
121
|
+
|
122
|
+
# Create the graph
|
123
|
+
dag_config = pipeline_config["dag"]
|
124
|
+
dag_hash = utils.get_dag_hash(dag_config)
|
125
|
+
dag = graph.create_graph(dag_config)
|
126
|
+
|
127
|
+
run_context.pipeline_file = pipeline_file
|
128
|
+
run_context.dag = dag
|
129
|
+
run_context.dag_hash = dag_hash
|
130
|
+
|
131
|
+
run_context.use_cached = False
|
132
|
+
if use_cached:
|
133
|
+
run_context.use_cached = True
|
134
|
+
run_context.original_run_id = use_cached
|
135
|
+
|
136
|
+
context.run_context = run_context
|
137
|
+
|
138
|
+
return run_context
|
139
|
+
|
140
|
+
|
141
|
+
def execute(
|
142
|
+
configuration_file: str,
|
143
|
+
pipeline_file: str,
|
144
|
+
tag: str = "",
|
145
|
+
run_id: str = "",
|
146
|
+
use_cached: str = "",
|
147
|
+
parameters_file: str = "",
|
148
|
+
):
|
149
|
+
# pylint: disable=R0914,R0913
|
150
|
+
"""
|
151
|
+
The entry point to magnus execution. This method would prepare the configurations and delegates traversal to the
|
152
|
+
executor
|
153
|
+
|
154
|
+
Args:
|
155
|
+
pipeline_file (str): The config/dag file
|
156
|
+
run_id (str): The run id of the run.
|
157
|
+
tag (str): If a tag is provided at the run time
|
158
|
+
use_cached (str): The previous run_id to use.
|
159
|
+
parameters_file (str): The parameters being sent in to the application
|
160
|
+
"""
|
161
|
+
# Re run settings
|
162
|
+
run_id = utils.generate_run_id(run_id=run_id)
|
163
|
+
|
164
|
+
run_context = prepare_configurations(
|
165
|
+
configuration_file=configuration_file,
|
166
|
+
pipeline_file=pipeline_file,
|
167
|
+
run_id=run_id,
|
168
|
+
tag=tag,
|
169
|
+
use_cached=use_cached,
|
170
|
+
parameters_file=parameters_file,
|
171
|
+
)
|
172
|
+
print("Working with context:")
|
173
|
+
print(run_context)
|
174
|
+
|
175
|
+
executor = run_context.executor
|
176
|
+
|
177
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
|
178
|
+
|
179
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
180
|
+
|
181
|
+
# Prepare for graph execution
|
182
|
+
executor.prepare_for_graph_execution()
|
183
|
+
|
184
|
+
logger.info("Executing the graph")
|
185
|
+
executor.execute_graph(dag=run_context.dag) # type: ignore
|
186
|
+
|
187
|
+
executor.send_return_code()
|
188
|
+
|
189
|
+
|
190
|
+
def execute_single_node(
|
191
|
+
configuration_file: str,
|
192
|
+
pipeline_file: str,
|
193
|
+
step_name: str,
|
194
|
+
map_variable: str,
|
195
|
+
run_id: str,
|
196
|
+
tag: str = "",
|
197
|
+
parameters_file: str = "",
|
198
|
+
):
|
199
|
+
"""
|
200
|
+
The entry point into executing a single node of magnus. Orchestration modes should extensively use this
|
201
|
+
entry point.
|
202
|
+
|
203
|
+
It should have similar set up of configurations to execute because orchestrator modes can initiate the execution.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
variables_file (str): The variables file, if used or None
|
207
|
+
step_name : The name of the step to execute in dot path convention
|
208
|
+
pipeline_file (str): The config/dag file
|
209
|
+
run_id (str): The run id of the run.
|
210
|
+
tag (str): If a tag is provided at the run time
|
211
|
+
parameters_file (str): The parameters being sent in to the application
|
212
|
+
|
213
|
+
"""
|
214
|
+
from runnable import nodes
|
215
|
+
|
216
|
+
run_context = prepare_configurations(
|
217
|
+
configuration_file=configuration_file,
|
218
|
+
pipeline_file=pipeline_file,
|
219
|
+
run_id=run_id,
|
220
|
+
tag=tag,
|
221
|
+
use_cached="",
|
222
|
+
parameters_file=parameters_file,
|
223
|
+
)
|
224
|
+
print("Working with context:")
|
225
|
+
print(run_context)
|
226
|
+
|
227
|
+
executor = run_context.executor
|
228
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
|
229
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
230
|
+
|
231
|
+
executor.prepare_for_node_execution()
|
232
|
+
|
233
|
+
if not run_context.dag:
|
234
|
+
# There are a few entry points that make graph dynamically and do not have a dag defined statically.
|
235
|
+
run_log = run_context.run_log_store.get_run_log_by_id(run_id=run_id, full=False)
|
236
|
+
run_context.dag = graph.create_graph(run_log.run_config["pipeline"])
|
237
|
+
|
238
|
+
step_internal_name = nodes.BaseNode._get_internal_name_from_command_name(step_name)
|
239
|
+
|
240
|
+
map_variable_dict = utils.json_to_ordered_dict(map_variable)
|
241
|
+
|
242
|
+
node_to_execute, _ = graph.search_node_by_internal_name(run_context.dag, step_internal_name)
|
243
|
+
|
244
|
+
logger.info("Executing the single node of : %s", node_to_execute)
|
245
|
+
executor.execute_node(node=node_to_execute, map_variable=map_variable_dict)
|
246
|
+
|
247
|
+
executor.send_return_code(stage="execution")
|
248
|
+
|
249
|
+
|
250
|
+
def execute_single_brach(
|
251
|
+
configuration_file: str,
|
252
|
+
pipeline_file: str,
|
253
|
+
branch_name: str,
|
254
|
+
map_variable: str,
|
255
|
+
run_id: str,
|
256
|
+
tag: str,
|
257
|
+
):
|
258
|
+
"""
|
259
|
+
The entry point into executing a branch of the graph. Interactive modes in parallel runs use this to execute
|
260
|
+
branches in parallel.
|
261
|
+
|
262
|
+
This entry point is never used by its own but rather from a node. So the arguments sent into this are fewer.
|
263
|
+
|
264
|
+
Args:
|
265
|
+
variables_file (str): The variables file, if used or None
|
266
|
+
branch_name : The name of the branch to execute, in dot.path.convention
|
267
|
+
pipeline_file (str): The config/dag file
|
268
|
+
run_id (str): The run id of the run.
|
269
|
+
tag (str): If a tag is provided at the run time
|
270
|
+
"""
|
271
|
+
from runnable import nodes
|
272
|
+
|
273
|
+
run_context = prepare_configurations(
|
274
|
+
configuration_file=configuration_file,
|
275
|
+
pipeline_file=pipeline_file,
|
276
|
+
run_id=run_id,
|
277
|
+
tag=tag,
|
278
|
+
use_cached="",
|
279
|
+
)
|
280
|
+
print("Working with context:")
|
281
|
+
print(run_context)
|
282
|
+
|
283
|
+
executor = run_context.executor
|
284
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
|
285
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
286
|
+
|
287
|
+
branch_internal_name = nodes.BaseNode._get_internal_name_from_command_name(branch_name)
|
288
|
+
|
289
|
+
map_variable_dict = utils.json_to_ordered_dict(map_variable)
|
290
|
+
|
291
|
+
branch_to_execute = graph.search_branch_by_internal_name(run_context.dag, branch_internal_name) # type: ignore
|
292
|
+
|
293
|
+
logger.info("Executing the single branch of %s", branch_to_execute)
|
294
|
+
executor.execute_graph(dag=branch_to_execute, map_variable=map_variable_dict)
|
295
|
+
|
296
|
+
executor.send_return_code()
|
297
|
+
|
298
|
+
|
299
|
+
def execute_notebook(
|
300
|
+
entrypoint: str,
|
301
|
+
notebook_file: str,
|
302
|
+
catalog_config: dict,
|
303
|
+
configuration_file: str,
|
304
|
+
notebook_output_path: str = "",
|
305
|
+
tag: str = "",
|
306
|
+
run_id: str = "",
|
307
|
+
parameters_file: str = "",
|
308
|
+
):
|
309
|
+
"""
|
310
|
+
The entry point to magnus execution of a notebook. This method would prepare the configurations and
|
311
|
+
delegates traversal to the executor
|
312
|
+
"""
|
313
|
+
run_id = utils.generate_run_id(run_id=run_id)
|
314
|
+
|
315
|
+
run_context = prepare_configurations(
|
316
|
+
configuration_file=configuration_file,
|
317
|
+
run_id=run_id,
|
318
|
+
tag=tag,
|
319
|
+
parameters_file=parameters_file,
|
320
|
+
)
|
321
|
+
|
322
|
+
executor = run_context.executor
|
323
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.UNCHAINED.value
|
324
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
325
|
+
|
326
|
+
print("Working with context:")
|
327
|
+
print(run_context)
|
328
|
+
|
329
|
+
step_config = {
|
330
|
+
"command": notebook_file,
|
331
|
+
"command_type": "notebook",
|
332
|
+
"notebook_output_path": notebook_output_path,
|
333
|
+
"type": "task",
|
334
|
+
"next": "success",
|
335
|
+
"catalog": catalog_config,
|
336
|
+
}
|
337
|
+
node = graph.create_node(name="executing job", step_config=step_config)
|
338
|
+
|
339
|
+
if entrypoint == defaults.ENTRYPOINT.USER.value:
|
340
|
+
# Prepare for graph execution
|
341
|
+
executor.prepare_for_graph_execution()
|
342
|
+
|
343
|
+
logger.info("Executing the job from the user. We are still in the caller's compute environment")
|
344
|
+
executor.execute_job(node=node)
|
345
|
+
|
346
|
+
elif entrypoint == defaults.ENTRYPOINT.SYSTEM.value:
|
347
|
+
executor.prepare_for_node_execution()
|
348
|
+
logger.info("Executing the job from the system. We are in the config's compute environment")
|
349
|
+
executor.execute_node(node=node)
|
350
|
+
|
351
|
+
# Update the status of the run log
|
352
|
+
step_log = run_context.run_log_store.get_step_log(node._get_step_log_name(), run_id)
|
353
|
+
run_context.run_log_store.update_run_log_status(run_id=run_id, status=step_log.status)
|
354
|
+
|
355
|
+
else:
|
356
|
+
raise ValueError(f"Invalid entrypoint {entrypoint}")
|
357
|
+
|
358
|
+
executor.send_return_code()
|
359
|
+
|
360
|
+
|
361
|
+
def execute_function(
|
362
|
+
entrypoint: str,
|
363
|
+
command: str,
|
364
|
+
catalog_config: dict,
|
365
|
+
configuration_file: str,
|
366
|
+
tag: str = "",
|
367
|
+
run_id: str = "",
|
368
|
+
parameters_file: str = "",
|
369
|
+
):
|
370
|
+
"""
|
371
|
+
The entry point to magnus execution of a function. This method would prepare the configurations and
|
372
|
+
delegates traversal to the executor
|
373
|
+
"""
|
374
|
+
run_id = utils.generate_run_id(run_id=run_id)
|
375
|
+
|
376
|
+
run_context = prepare_configurations(
|
377
|
+
configuration_file=configuration_file,
|
378
|
+
run_id=run_id,
|
379
|
+
tag=tag,
|
380
|
+
parameters_file=parameters_file,
|
381
|
+
)
|
382
|
+
|
383
|
+
executor = run_context.executor
|
384
|
+
|
385
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.UNCHAINED.value
|
386
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
387
|
+
|
388
|
+
print("Working with context:")
|
389
|
+
print(run_context)
|
390
|
+
|
391
|
+
# Prepare the graph with a single node
|
392
|
+
step_config = {
|
393
|
+
"command": command,
|
394
|
+
"command_type": "python",
|
395
|
+
"type": "task",
|
396
|
+
"next": "success",
|
397
|
+
"catalog": catalog_config,
|
398
|
+
}
|
399
|
+
node = graph.create_node(name="executing job", step_config=step_config)
|
400
|
+
|
401
|
+
if entrypoint == defaults.ENTRYPOINT.USER.value:
|
402
|
+
# Prepare for graph execution
|
403
|
+
executor.prepare_for_graph_execution()
|
404
|
+
|
405
|
+
logger.info("Executing the job from the user. We are still in the caller's compute environment")
|
406
|
+
executor.execute_job(node=node)
|
407
|
+
|
408
|
+
elif entrypoint == defaults.ENTRYPOINT.SYSTEM.value:
|
409
|
+
executor.prepare_for_node_execution()
|
410
|
+
logger.info("Executing the job from the system. We are in the config's compute environment")
|
411
|
+
executor.execute_node(node=node)
|
412
|
+
|
413
|
+
# Update the status of the run log
|
414
|
+
step_log = run_context.run_log_store.get_step_log(node._get_step_log_name(), run_id)
|
415
|
+
run_context.run_log_store.update_run_log_status(run_id=run_id, status=step_log.status)
|
416
|
+
|
417
|
+
else:
|
418
|
+
raise ValueError(f"Invalid entrypoint {entrypoint}")
|
419
|
+
|
420
|
+
executor.send_return_code()
|
421
|
+
|
422
|
+
|
423
|
+
def fan(
|
424
|
+
configuration_file: str,
|
425
|
+
pipeline_file: str,
|
426
|
+
step_name: str,
|
427
|
+
mode: str,
|
428
|
+
map_variable: str,
|
429
|
+
run_id: str,
|
430
|
+
tag: str = "",
|
431
|
+
parameters_file: str = "",
|
432
|
+
):
|
433
|
+
"""
|
434
|
+
The entry point to either fan in or out for a composite node. Only 3rd party orchestrators should use this.
|
435
|
+
|
436
|
+
It should have similar set up of configurations to execute because orchestrator modes can initiate the execution.
|
437
|
+
|
438
|
+
Args:
|
439
|
+
configuration_file (str): The configuration file.
|
440
|
+
mode: in or out
|
441
|
+
step_name : The name of the step to execute in dot path convention
|
442
|
+
pipeline_file (str): The config/dag file
|
443
|
+
run_id (str): The run id of the run.
|
444
|
+
tag (str): If a tag is provided at the run time
|
445
|
+
parameters_file (str): The parameters being sent in to the application
|
446
|
+
|
447
|
+
"""
|
448
|
+
from runnable import nodes
|
449
|
+
|
450
|
+
run_context = prepare_configurations(
|
451
|
+
configuration_file=configuration_file,
|
452
|
+
pipeline_file=pipeline_file,
|
453
|
+
run_id=run_id,
|
454
|
+
tag=tag,
|
455
|
+
use_cached="",
|
456
|
+
parameters_file=parameters_file,
|
457
|
+
)
|
458
|
+
print("Working with context:")
|
459
|
+
print(run_context)
|
460
|
+
|
461
|
+
executor = run_context.executor
|
462
|
+
run_context.execution_plan = defaults.EXECUTION_PLAN.CHAINED.value
|
463
|
+
utils.set_magnus_environment_variables(run_id=run_id, configuration_file=configuration_file, tag=tag)
|
464
|
+
|
465
|
+
executor.prepare_for_node_execution()
|
466
|
+
|
467
|
+
step_internal_name = nodes.BaseNode._get_internal_name_from_command_name(step_name)
|
468
|
+
node_to_execute, _ = graph.search_node_by_internal_name(run_context.dag, step_internal_name) # type: ignore
|
469
|
+
|
470
|
+
map_variable_dict = utils.json_to_ordered_dict(map_variable)
|
471
|
+
|
472
|
+
if mode == "in":
|
473
|
+
logger.info("Fanning in for : %s", node_to_execute)
|
474
|
+
executor.fan_in(node=node_to_execute, map_variable=map_variable_dict)
|
475
|
+
elif mode == "out":
|
476
|
+
logger.info("Fanning out for : %s", node_to_execute)
|
477
|
+
executor.fan_out(node=node_to_execute, map_variable=map_variable_dict)
|
478
|
+
else:
|
479
|
+
raise ValueError(f"Invalid mode {mode}")
|
480
|
+
|
481
|
+
|
482
|
+
if __name__ == "__main__":
|
483
|
+
# This is only for perf testing purposes.
|
484
|
+
prepare_configurations(run_id="abc", pipeline_file="example/mocking.yaml")
|