runnable 0.18.0__py3-none-any.whl → 0.19.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -101,23 +101,40 @@ class HostPath(BaseModel):
101
101
  path: str
102
102
 
103
103
 
104
- class Volume(BaseModel):
104
+ class HostPathVolume(BaseModel):
105
105
  name: str
106
106
  host_path: HostPath
107
107
 
108
108
 
109
- class TemplateSpec(BaseModel):
109
+ class PVCClaim(BaseModel):
110
+ claim_name: str
111
+
112
+ model_config = ConfigDict(
113
+ alias_generator=to_camel,
114
+ populate_by_name=True,
115
+ from_attributes=True,
116
+ )
117
+
118
+
119
+ class PVCVolume(BaseModel):
120
+ name: str
121
+ persistent_volume_claim: PVCClaim
122
+
123
+
124
+ class K8sTemplateSpec(BaseModel):
110
125
  active_deadline_seconds: int = Field(default=60 * 60 * 2) # 2 hours
111
126
  node_selector: Optional[dict[str, str]] = None
112
127
  tolerations: Optional[list[dict[str, str]]] = None
113
- volumes: Optional[list[Volume]] = Field(default_factory=lambda: [])
128
+ volumes: Optional[list[HostPathVolume | PVCVolume]] = Field(
129
+ default_factory=lambda: []
130
+ )
114
131
  service_account_name: Optional[str] = "default"
115
132
  restart_policy: RestartPolicy = RestartPolicy.NEVER
116
133
  container: Container
117
134
 
118
135
 
119
- class Template(BaseModel):
120
- spec: TemplateSpec
136
+ class K8sTemplate(BaseModel):
137
+ spec: K8sTemplateSpec
121
138
  metadata: Optional[ObjectMetaData] = None
122
139
 
123
140
 
@@ -125,32 +142,25 @@ class Spec(BaseModel):
125
142
  active_deadline_seconds: Optional[int] = Field(default=60 * 60 * 2) # 2 hours
126
143
  backoff_limit: int = 6
127
144
  selector: Optional[LabelSelector] = None
128
- template: Template
145
+ template: K8sTemplate
129
146
  ttl_seconds_after_finished: Optional[int] = Field(default=60 * 60 * 24) # 24 hours
130
147
 
131
148
 
132
- class K8sJobExecutor(GenericJobExecutor):
149
+ class GenericK8sJobExecutor(GenericJobExecutor):
133
150
  service_name: str = "k8s-job"
134
151
  config_path: Optional[str] = None
135
152
  job_spec: Spec
136
153
  mock: bool = False
137
-
138
- # The location the mount of .run_log_store is mounted to in minikube
139
- # ensure that minikube mount $HOME/workspace/runnable/.run_log_store:/volume/run_logs is executed first
140
- # $HOME/workspace/runnable/.catalog:/volume/catalog
141
- # Ensure that the docker build is done with eval $(minikube docker-env)
142
- mini_k8s_run_log_location: str = Field(default="/volume/run_logs/")
143
- mini_k8s_catalog_location: str = Field(default="/volume/catalog/")
154
+ namespace: str = Field(default="default")
144
155
 
145
156
  _is_local: bool = PrivateAttr(default=False)
157
+ _volume_mounts: list[VolumeMount] = PrivateAttr(default_factory=lambda: [])
158
+ _volumes: list[HostPathVolume | PVCVolume] = PrivateAttr(default_factory=lambda: [])
146
159
 
147
160
  _container_log_location: str = PrivateAttr(default="/tmp/run_logs/")
148
161
  _container_catalog_location: str = PrivateAttr(default="/tmp/catalog/")
149
162
  _container_secrets_location: str = PrivateAttr(default="/tmp/dotenv")
150
163
 
151
- _volumes: list[Volume] = []
152
- _volume_mounts: list[VolumeMount] = []
153
-
154
164
  model_config = ConfigDict(
155
165
  alias_generator=to_camel,
156
166
  populate_by_name=True,
@@ -287,14 +297,17 @@ class K8sJobExecutor(GenericJobExecutor):
287
297
  )
288
298
 
289
299
  logger.info(f"Submitting job: {job.__dict__}")
300
+ if self.mock:
301
+ print(job.__dict__)
302
+ return
290
303
 
291
304
  try:
292
305
  k8s_batch = self._client.BatchV1Api()
293
306
  response = k8s_batch.create_namespaced_job(
294
307
  body=job,
295
- namespace="default",
296
308
  _preload_content=False,
297
309
  pretty=True,
310
+ namespace=self.namespace,
298
311
  )
299
312
  logger.debug(f"Kubernetes job response: {response}")
300
313
  except Exception as e:
@@ -302,6 +315,43 @@ class K8sJobExecutor(GenericJobExecutor):
302
315
  print(e)
303
316
  raise
304
317
 
318
+ def _create_volumes(self): ...
319
+
320
+ def _use_volumes(self):
321
+ match self._context.run_log_store.service_name:
322
+ case "file-system":
323
+ self._context.run_log_store.log_folder = self._container_log_location
324
+ case "chunked-fs":
325
+ self._context.run_log_store.log_folder = self._container_log_location
326
+
327
+ match self._context.catalog_handler.service_name:
328
+ case "file-system":
329
+ self._context.catalog_handler.catalog_location = (
330
+ self._container_catalog_location
331
+ )
332
+
333
+
334
+ class MiniK8sJobExecutor(GenericK8sJobExecutor):
335
+ service_name: str = "k8s-job"
336
+ config_path: Optional[str] = None
337
+ job_spec: Spec
338
+ mock: bool = False
339
+
340
+ # The location the mount of .run_log_store is mounted to in minikube
341
+ # ensure that minikube mount $HOME/workspace/runnable/.run_log_store:/volume/run_logs is executed first
342
+ # $HOME/workspace/runnable/.catalog:/volume/catalog
343
+ # Ensure that the docker build is done with eval $(minikube docker-env)
344
+ mini_k8s_run_log_location: str = Field(default="/volume/run_logs/")
345
+ mini_k8s_catalog_location: str = Field(default="/volume/catalog/")
346
+
347
+ _is_local: bool = PrivateAttr(default=False)
348
+
349
+ model_config = ConfigDict(
350
+ alias_generator=to_camel,
351
+ populate_by_name=True,
352
+ from_attributes=True,
353
+ )
354
+
305
355
  def _create_volumes(self):
306
356
  match self._context.run_log_store.service_name:
307
357
  case "file-system":
@@ -311,7 +361,7 @@ class K8sJobExecutor(GenericJobExecutor):
311
361
  # You then are creating a volume that is mounted to /tmp/run_logs in the container
312
362
  # You are then referring to it.
313
363
  # https://stackoverflow.com/questions/57411456/minikube-mounted-host-folders-are-not-working
314
- Volume(
364
+ HostPathVolume(
315
365
  name="run-logs",
316
366
  host_path=HostPath(path=self.mini_k8s_run_log_location),
317
367
  )
@@ -323,7 +373,7 @@ class K8sJobExecutor(GenericJobExecutor):
323
373
  )
324
374
  case "chunked-fs":
325
375
  self._volumes.append(
326
- Volume(
376
+ HostPathVolume(
327
377
  name="run-logs",
328
378
  host_path=HostPath(path=self.mini_k8s_run_log_location),
329
379
  )
@@ -337,7 +387,7 @@ class K8sJobExecutor(GenericJobExecutor):
337
387
  match self._context.catalog_handler.service_name:
338
388
  case "file-system":
339
389
  self._volumes.append(
340
- Volume(
390
+ HostPathVolume(
341
391
  name="catalog",
342
392
  host_path=HostPath(path=self.mini_k8s_catalog_location),
343
393
  )
@@ -348,15 +398,87 @@ class K8sJobExecutor(GenericJobExecutor):
348
398
  )
349
399
  )
350
400
 
351
- def _use_volumes(self):
401
+
402
+ class K8sJobExecutor(GenericK8sJobExecutor):
403
+ service_name: str = "k8s-job"
404
+ config_path: Optional[str] = None
405
+ job_spec: Spec
406
+ mock: bool = False
407
+ pvc_claim_name: str
408
+
409
+ # change the spec to pull image if not present
410
+ def model_post_init(self, __context):
411
+ self.job_spec.template.spec.container.image_pull_policy = ImagePullPolicy.ALWAYS
412
+
413
+ _is_local: bool = PrivateAttr(default=False)
414
+
415
+ model_config = ConfigDict(
416
+ alias_generator=to_camel,
417
+ populate_by_name=True,
418
+ from_attributes=True,
419
+ )
420
+
421
+ def execute_job(self, job: BaseTaskType, catalog_settings=Optional[List[str]]):
422
+ self._use_volumes()
423
+ self._set_up_run_log()
424
+
425
+ job_log = self._context.run_log_store.create_job_log()
426
+ self._context.run_log_store.add_job_log(
427
+ run_id=self._context.run_id, job_log=job_log
428
+ )
429
+
430
+ job_log = self._context.run_log_store.get_job_log(run_id=self._context.run_id)
431
+
432
+ attempt_log = job.execute_command(
433
+ attempt_number=self.step_attempt_number,
434
+ mock=self.mock,
435
+ )
436
+
437
+ job_log.status = attempt_log.status
438
+ job_log.attempts.append(attempt_log)
439
+
440
+ data_catalogs_put: Optional[List[DataCatalog]] = self._sync_catalog(
441
+ catalog_settings=catalog_settings
442
+ )
443
+ logger.debug(f"data_catalogs_put: {data_catalogs_put}")
444
+
445
+ job_log.add_data_catalogs(data_catalogs_put or [])
446
+
447
+ console.print("Summary of job")
448
+ console.print(job_log.get_summary())
449
+
450
+ self._context.run_log_store.add_job_log(
451
+ run_id=self._context.run_id, job_log=job_log
452
+ )
453
+
454
+ def _create_volumes(self):
455
+ self._volumes.append(
456
+ PVCVolume(
457
+ name=self.pvc_claim_name,
458
+ persistent_volume_claim=PVCClaim(claim_name=self.pvc_claim_name),
459
+ )
460
+ )
352
461
  match self._context.run_log_store.service_name:
353
462
  case "file-system":
354
- self._context.run_log_store.log_folder = self._container_log_location
463
+ self._volume_mounts.append(
464
+ VolumeMount(
465
+ name=self.pvc_claim_name,
466
+ mount_path=self._container_log_location,
467
+ )
468
+ )
355
469
  case "chunked-fs":
356
- self._context.run_log_store.log_folder = self._container_log_location
470
+ self._volume_mounts.append(
471
+ VolumeMount(
472
+ name=self.pvc_claim_name,
473
+ mount_path=self._container_log_location,
474
+ )
475
+ )
357
476
 
358
477
  match self._context.catalog_handler.service_name:
359
478
  case "file-system":
360
- self._context.catalog_handler.catalog_location = (
361
- self._container_catalog_location
479
+ self._volume_mounts.append(
480
+ VolumeMount(
481
+ name=self.pvc_claim_name,
482
+ mount_path=self._container_catalog_location,
483
+ )
362
484
  )
@@ -268,7 +268,6 @@ class LocalContainerExecutor(GenericPipelineExecutor):
268
268
  f"Please provide a docker_image using executor_config of the step {node.name} or at global config"
269
269
  )
270
270
 
271
- # TODO: Should consider using getpass.getuser() when running the docker container? Volume permissions
272
271
  container = client.containers.create(
273
272
  image=docker_image,
274
273
  command=command,
runnable/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # ruff: noqa
2
2
 
3
+
3
4
  import logging
4
5
  import os
5
6
  from logging.config import dictConfig
runnable/catalog.py CHANGED
@@ -10,8 +10,6 @@ from runnable.datastore import DataCatalog
10
10
 
11
11
  logger = logging.getLogger(defaults.LOGGER_NAME)
12
12
 
13
- # TODO: Should ** be allowed as glob pattern as it can potentially copy everything to catalog
14
-
15
13
 
16
14
  def is_catalog_out_of_sync(
17
15
  catalog, synced_catalogs=Optional[List[DataCatalog]]
@@ -170,3 +168,4 @@ class DoNothingCatalog(BaseCatalog):
170
168
  Does nothing
171
169
  """
172
170
  logger.info("Using a do-nothing catalog, doing nothing while sync between runs")
171
+ logger.info("Using a do-nothing catalog, doing nothing while sync between runs")
runnable/entrypoints.py CHANGED
@@ -16,9 +16,6 @@ from runnable.executor import BaseJobExecutor, BasePipelineExecutor
16
16
  logger = logging.getLogger(defaults.LOGGER_NAME)
17
17
 
18
18
 
19
- print("") # removes the buffer print
20
-
21
-
22
19
  def get_default_configs() -> RunnableConfig:
23
20
  """
24
21
  User can provide extensions as part of their code base, runnable-config.yaml provides the place to put them.
@@ -128,11 +125,10 @@ def prepare_configurations(
128
125
  "job-executor", None
129
126
  ) # type: ignore
130
127
  if not job_executor_config:
131
- executor_config = cast(
128
+ job_executor_config = cast(
132
129
  ServiceConfig,
133
130
  runnable_defaults.get("job-executor", defaults.DEFAULT_JOB_EXECUTOR),
134
131
  )
135
-
136
132
  assert job_executor_config, "Job executor is not provided"
137
133
  configured_executor = utils.get_provider_by_name_and_type(
138
134
  "job_executor", job_executor_config
runnable/executor.py CHANGED
@@ -11,9 +11,9 @@ import runnable.context as context
11
11
  from runnable import defaults
12
12
  from runnable.datastore import DataCatalog, JobLog, StepLog
13
13
  from runnable.defaults import TypeMapVariable
14
- from runnable.graph import Graph
15
14
 
16
15
  if TYPE_CHECKING: # pragma: no cover
16
+ from runnable.graph import Graph
17
17
  from runnable.nodes import BaseNode
18
18
  from runnable.tasks import BaseTaskType
19
19
 
runnable/parameters.py CHANGED
@@ -15,8 +15,6 @@ from runnable.utils import remove_prefix
15
15
 
16
16
  logger = logging.getLogger(defaults.LOGGER_NAME)
17
17
 
18
- # TODO: Revisit this, it might be a bit too complicated than required
19
-
20
18
 
21
19
  def get_user_set_parameters(remove: bool = False) -> Dict[str, JsonParameter]:
22
20
  """
@@ -50,13 +48,6 @@ def get_user_set_parameters(remove: bool = False) -> Dict[str, JsonParameter]:
50
48
  return parameters
51
49
 
52
50
 
53
- def serialize_parameter_as_str(value: Any) -> str:
54
- if isinstance(value, BaseModel):
55
- return json.dumps(value.model_dump())
56
-
57
- return json.dumps(value)
58
-
59
-
60
51
  def filter_arguments_for_func(
61
52
  func: Callable[..., Any],
62
53
  params: Dict[str, Any],
runnable/utils.py CHANGED
@@ -17,7 +17,7 @@ from ruamel.yaml import YAML
17
17
  from stevedore import driver
18
18
 
19
19
  import runnable.context as context
20
- from runnable import defaults, names
20
+ from runnable import console, defaults, names
21
21
  from runnable.defaults import TypeMapVariable
22
22
 
23
23
  if TYPE_CHECKING: # pragma: no cover
@@ -176,7 +176,7 @@ def is_a_git_repo() -> bool:
176
176
  logger.info("Found the code to be git versioned")
177
177
  return True
178
178
  except BaseException: # pylint: disable=W0702
179
- logger.error("No git repo found, unsafe hash")
179
+ console.print("Not a git repo", style="bold red")
180
180
 
181
181
  return False
182
182
 
@@ -195,27 +195,7 @@ def get_current_code_commit() -> Union[str, None]:
195
195
  logger.info("Found the git commit to be: %s", label)
196
196
  return label
197
197
  except BaseException: # pylint: disable=W0702
198
- logger.exception("Error getting git hash")
199
- raise
200
-
201
-
202
- def archive_git_tracked(name: str):
203
- """Generate a git archive of the tracked files.
204
-
205
- Args:
206
- name (str): The name to give the archive
207
-
208
- Raises:
209
- Exception: If its not a git repo
210
- """
211
- command = f"git archive -v -o {name}.tar.gz --format=tar.gz HEAD"
212
-
213
- if not is_a_git_repo():
214
- raise Exception("Not a git repo")
215
- try:
216
- subprocess.check_output(command.split()).strip().decode("utf-8")
217
- except BaseException: # pylint: disable=W0702
218
- logger.exception("Error archiving repo")
198
+ console.print("Not a git repo, error getting hash", style="bold red")
219
199
  raise
220
200
 
221
201
 
@@ -234,7 +214,7 @@ def is_git_clean() -> Tuple[bool, Union[None, str]]:
234
214
  return True, None
235
215
  return False, label
236
216
  except BaseException: # pylint: disable=W0702
237
- logger.exception("Error checking if the code is git clean")
217
+ console.print("Not a git repo, not clean", style="bold red")
238
218
 
239
219
  return False, None
240
220
 
@@ -253,7 +233,7 @@ def get_git_remote() -> Union[str, None]:
253
233
  logger.info("Found the git remote to be: %s", label)
254
234
  return label
255
235
  except BaseException: # pylint: disable=W0702
256
- logger.exception("Error getting git remote")
236
+ console.print("Not a git repo, no remote", style="bold red")
257
237
  raise
258
238
 
259
239
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runnable
3
- Version: 0.18.0
3
+ Version: 0.19.0
4
4
  Summary: Add your description here
5
5
  Author-email: "Vammi, Vijay" <vijay.vammi@astrazeneca.com>
6
6
  License-File: LICENSE
@@ -5,7 +5,7 @@ extensions/catalog/file_system.py,sha256=VZEUx4X-GDSM8rJ_2kiCOyw1eek3roN0CiSB8wd
5
5
  extensions/catalog/pyproject.toml,sha256=lLNxY6v04c8I5QK_zKw_E6sJTArSJRA_V-79ktaA3Hk,279
6
6
  extensions/job_executor/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  extensions/job_executor/__init__.py,sha256=HINaPjBWz04Ni7GqhuDLi0lS0-gYzq52HcOioYueYJE,5513
8
- extensions/job_executor/k8s.py,sha256=BHICgJ_TT0JoGKPfX5GH_DpflKtIcSHp8I9i1ycSRZo,11516
8
+ extensions/job_executor/k8s.py,sha256=huffLcDIQgTU-Qsz05CekrIwqFZLtFRGFlQAplwPRoE,15258
9
9
  extensions/job_executor/k8s_job_spec.yaml,sha256=7aFpxHdO_p6Hkc3YxusUOuAQTD1Myu0yTPX9DrhxbOg,1158
10
10
  extensions/job_executor/local.py,sha256=8ebu4TKo6FnFiUflil6fmE7Pk8eSoe1fHNwX8YwI1BQ,1865
11
11
  extensions/job_executor/local_container.py,sha256=7G2ARgoPwXbpNPgd5UDxWZqU2ABPIAk7bkNQkC4cNBQ,6585
@@ -18,7 +18,7 @@ extensions/pipeline_executor/__init__.py,sha256=YnKILiy-SxfnG3rYUoinjh1lfkuAF5QX
18
18
  extensions/pipeline_executor/argo.py,sha256=ClfuU_Of_2f5mvqVgY1QQwwJwXHB0LbzwNArG1x2Axc,44666
19
19
  extensions/pipeline_executor/argo_specification.yaml,sha256=wXQcm2gOQYqy-IOQIhucohS32ZrHKCfGA5zZ0RraPYc,1276
20
20
  extensions/pipeline_executor/local.py,sha256=H8s6AdML_9_f-vdGG_6k0y9FbLqAqvA1S_7xMNyARzY,1946
21
- extensions/pipeline_executor/local_container.py,sha256=hqxLkxBOzk8P_PYnnZw5Ve9K0ztoyedv4kUVLkz8vVY,13967
21
+ extensions/pipeline_executor/local_container.py,sha256=UCap8wCbHrtTN5acECBBkvcXkA3SXtrAOGW88JT7ofw,13853
22
22
  extensions/pipeline_executor/mocked.py,sha256=SuObJ6Myt7p8duW8sylIp1cYIAnFutsJW1avWaOUY3c,5798
23
23
  extensions/pipeline_executor/pyproject.toml,sha256=ykTX7srR10PBYb8LsIwEj8vIPPIEZQ5V_R7VYbZ-ido,291
24
24
  extensions/pipeline_executor/retry.py,sha256=KGenhWrLLmOQgzMvqloXHDRJyoNs91t05rRW8aLW6FA,6969
@@ -33,26 +33,26 @@ extensions/run_log_store/db/integration_FF.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
33
33
  extensions/secrets/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  extensions/secrets/dotenv.py,sha256=FbYYd_pVuJuVuIDIvXbzKuSSQ9GPq7xJXTDbJMTQbhM,1583
35
35
  extensions/secrets/pyproject.toml,sha256=mLJNImNcBlbLKHh-0ugVWT9V83R4RibyyYDtBCSqVF4,282
36
- runnable/__init__.py,sha256=KqpLDTD1CfdEj2aDyEkSn2KW-_83qyrRrrWLc5lZVM4,624
37
- runnable/catalog.py,sha256=MiEmb-18liAKmgeMdDF41VVn0ZEAVLP8hR33oacQ1zs,4930
36
+ runnable/__init__.py,sha256=fYkOrbsb-E1rGkrof7kOJ3KboTFH-HriGa-8npn4-50,625
37
+ runnable/catalog.py,sha256=b9N40kTv1IBidzlWjkHcBGyYhq6qIDHZfBuFenzjsMI,4924
38
38
  runnable/cli.py,sha256=01zmzOdynEmLI4vWDtSHQ6y1od_Jlc8G1RF69fi2L8g,8446
39
39
  runnable/context.py,sha256=by5uepmuCP0dmM9BmsliXihSes5QEFejwAsmekcqylE,1388
40
40
  runnable/datastore.py,sha256=9y5enzn6AXLHLdwvgkdjGPrBkVlrcjfbaAHsst-lJzg,32466
41
41
  runnable/defaults.py,sha256=3o9IVGryyCE6PoQTOoaIaHHTbJGEzmdXMcwzOhwAYoI,3518
42
- runnable/entrypoints.py,sha256=67gPBiIIS4Kd9g6LdoGCraRJPda8K1i7Lp7XcD2iY5k,18913
42
+ runnable/entrypoints.py,sha256=P958nFz5WAsgTwd9sW04Q30vtjweYpr3rPsHVY4gh2U,18876
43
43
  runnable/exceptions.py,sha256=LFbp0-Qxg2PAMLEVt7w2whhBxSG-5pzUEv5qN-Rc4_c,3003
44
- runnable/executor.py,sha256=Rafu9EECrNq1LBkJmS6KYCekchP5ufrR04mHWG-JzqQ,15543
44
+ runnable/executor.py,sha256=ZPpfKwjDJnta03M2cWIINXcwke2ZDVc_QrIw7kwpHDQ,15547
45
45
  runnable/graph.py,sha256=jVjikRLR-so3b2ufmNKpEQ_Ny68qN4bcGDAdXBRKiCY,16574
46
46
  runnable/names.py,sha256=vn92Kv9ANROYSZX6Z4z1v_WA3WiEdIYmG6KEStBFZug,8134
47
47
  runnable/nodes.py,sha256=YU9u7r1ESzui1uVtJ1dgwdv1ozyJnF2k-MCFieT8CLI,17519
48
- runnable/parameters.py,sha256=g_bJurLjuppFDiDpfFqy6BRF36o_EY0OC5APl7HJFok,5450
48
+ runnable/parameters.py,sha256=LyQb1d0SaFeI4PJ_yDYt9wArm9ThSPASWb36TwIdDUs,5213
49
49
  runnable/pickler.py,sha256=ydJ_eti_U1F4l-YacFp7BWm6g5vTn04UXye25S1HVok,2684
50
50
  runnable/sdk.py,sha256=xN5F4XX8r5wCN131kgN2xG7MkNm0bSGJ3Ukw8prHYJ8,31444
51
51
  runnable/secrets.py,sha256=PXcEJw-4WPzeWRLfsatcPPyr1zkqgHzdRWRcS9vvpvM,2354
52
52
  runnable/tasks.py,sha256=JnIIYQf3YUidHXIN6hiUIfDnegc7_rJMNXuHW4WS9ig,29378
53
- runnable/utils.py,sha256=wqyN7lMW56cBqyE59iDE6_i2HXPkvEUCQ-66UQnIwTA,19993
54
- runnable-0.18.0.dist-info/METADATA,sha256=lBeCXTFGeVWpbxnOj2pZY6hTSNyB2Q5OWx5kVAF252s,9945
55
- runnable-0.18.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
- runnable-0.18.0.dist-info/entry_points.txt,sha256=I92DYldRrCb9HCsoum8GjC2UsQrWpuw2kawXTZpkIz4,1559
57
- runnable-0.18.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
58
- runnable-0.18.0.dist-info/RECORD,,
53
+ runnable/utils.py,sha256=Kwf54tHMVXYK7MCmvAi_FG08U_bHDKIQO-HDpM9X0QI,19500
54
+ runnable-0.19.0.dist-info/METADATA,sha256=tgXvJ1oDrhC59zVORaSTyFKqqLDp785wKWu55NUwSOE,9945
55
+ runnable-0.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
56
+ runnable-0.19.0.dist-info/entry_points.txt,sha256=seek5WVGvwYALm8lZ0TfPXwG5NaCeUKjU8urF8k3gvY,1621
57
+ runnable-0.19.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
58
+ runnable-0.19.0.dist-info/RECORD,,
@@ -9,6 +9,7 @@ file-system = extensions.catalog.file_system:FileSystemCatalog
9
9
  k8s-job = extensions.job_executor.k8s:K8sJobExecutor
10
10
  local = extensions.job_executor.local:LocalJobExecutor
11
11
  local-container = extensions.job_executor.local_container:LocalContainerJobExecutor
12
+ mini-k8s-job = extensions.job_executor.k8s:MiniK8sJobExecutor
12
13
 
13
14
  [nodes]
14
15
  dag = extensions.nodes.nodes:DagNode