outerbounds 0.3.175rc1__py3-none-any.whl → 0.3.176rc2__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.
- outerbounds/apps/app_cli.py +167 -5
- outerbounds/apps/capsule.py +105 -11
- outerbounds/apps/utils.py +26 -0
- outerbounds/command_groups/apps_cli.py +1 -1
- {outerbounds-0.3.175rc1.dist-info → outerbounds-0.3.176rc2.dist-info}/METADATA +3 -3
- {outerbounds-0.3.175rc1.dist-info → outerbounds-0.3.176rc2.dist-info}/RECORD +8 -8
- {outerbounds-0.3.175rc1.dist-info → outerbounds-0.3.176rc2.dist-info}/WHEEL +0 -0
- {outerbounds-0.3.175rc1.dist-info → outerbounds-0.3.176rc2.dist-info}/entry_points.txt +0 -0
outerbounds/apps/app_cli.py
CHANGED
@@ -13,14 +13,11 @@ from .app_config import (
|
|
13
13
|
AuthType,
|
14
14
|
)
|
15
15
|
from .cli_to_config import build_config_from_options
|
16
|
-
from .utils import
|
17
|
-
CommaSeparatedListType,
|
18
|
-
KVPairType,
|
19
|
-
)
|
16
|
+
from .utils import CommaSeparatedListType, KVPairType, KVDictType
|
20
17
|
from . import experimental
|
21
18
|
from .validations import deploy_validations
|
22
19
|
from .code_package import CodePackager
|
23
|
-
from .capsule import Capsule
|
20
|
+
from .capsule import Capsule, list_and_filter_capsules
|
24
21
|
import shlex
|
25
22
|
import time
|
26
23
|
import uuid
|
@@ -30,6 +27,8 @@ LOGGER_TIMESTAMP = "magenta"
|
|
30
27
|
LOGGER_COLOR = "green"
|
31
28
|
LOGGER_BAD_COLOR = "red"
|
32
29
|
|
30
|
+
NativeList = list
|
31
|
+
|
33
32
|
|
34
33
|
def _logger(
|
35
34
|
body="", system_msg=False, head="", bad=False, timestamp=True, nl=True, color=None
|
@@ -75,6 +74,36 @@ def _pre_create_debug(app_config: AppConfig, capsule: Capsule, state_dir: str):
|
|
75
74
|
)
|
76
75
|
|
77
76
|
|
77
|
+
def print_table(data, headers):
|
78
|
+
"""Print data in a formatted table."""
|
79
|
+
if not data:
|
80
|
+
return
|
81
|
+
|
82
|
+
# Calculate column widths
|
83
|
+
col_widths = [len(h) for h in headers]
|
84
|
+
|
85
|
+
# Calculate actual widths based on data
|
86
|
+
for row in data:
|
87
|
+
for i, cell in enumerate(row):
|
88
|
+
col_widths[i] = max(col_widths[i], len(str(cell)))
|
89
|
+
|
90
|
+
# Print header
|
91
|
+
header_row = " | ".join(
|
92
|
+
[headers[i].ljust(col_widths[i]) for i in range(len(headers))]
|
93
|
+
)
|
94
|
+
click.secho("-" * len(header_row), fg="yellow")
|
95
|
+
click.secho(header_row, fg="yellow", bold=True)
|
96
|
+
click.secho("-" * len(header_row), fg="yellow")
|
97
|
+
|
98
|
+
# Print data rows
|
99
|
+
for row in data:
|
100
|
+
formatted_row = " | ".join(
|
101
|
+
[str(row[i]).ljust(col_widths[i]) for i in range(len(row))]
|
102
|
+
)
|
103
|
+
click.secho(formatted_row, fg="green", bold=True)
|
104
|
+
click.secho("-" * len(header_row), fg="yellow")
|
105
|
+
|
106
|
+
|
78
107
|
@click.group()
|
79
108
|
def cli():
|
80
109
|
"""Outerbounds CLI tool."""
|
@@ -490,6 +519,139 @@ def deploy(ctx, command, **options):
|
|
490
519
|
raise e
|
491
520
|
|
492
521
|
|
522
|
+
def _parse_capsule_table(filtered_capsules):
|
523
|
+
headers = ["Name", "ID", "Ready", "App Type", "Port", "Tags", "URL"]
|
524
|
+
table_data = []
|
525
|
+
|
526
|
+
for capsule in filtered_capsules:
|
527
|
+
spec = capsule.get("spec", {})
|
528
|
+
status = capsule.get("status", {})
|
529
|
+
cap_id = capsule.get("id")
|
530
|
+
|
531
|
+
display_name = spec.get("displayName", "")
|
532
|
+
ready = str(status.get("readyToServeTraffic", False))
|
533
|
+
auth_type = spec.get("authConfig", {}).get("authType", "")
|
534
|
+
port = str(spec.get("port", ""))
|
535
|
+
tags_str = ", ".join(
|
536
|
+
[f"{tag['key']}={tag['value']}" for tag in spec.get("tags", [])]
|
537
|
+
)
|
538
|
+
url = status.get("accessInfo", {}).get("outOfClusterURL", "")
|
539
|
+
|
540
|
+
table_data.append(
|
541
|
+
[
|
542
|
+
display_name,
|
543
|
+
cap_id,
|
544
|
+
ready,
|
545
|
+
auth_type,
|
546
|
+
port,
|
547
|
+
tags_str,
|
548
|
+
f"https://{url}",
|
549
|
+
]
|
550
|
+
)
|
551
|
+
return headers, table_data
|
552
|
+
|
553
|
+
|
554
|
+
@app.command(help="List apps in the Outerbounds Platform.")
|
555
|
+
@click.option("--project", type=str, help="Filter apps by project")
|
556
|
+
@click.option("--branch", type=str, help="Filter apps by branch")
|
557
|
+
@click.option("--name", type=str, help="Filter apps by name")
|
558
|
+
@click.option(
|
559
|
+
"--tag",
|
560
|
+
"tags",
|
561
|
+
type=KVDictType,
|
562
|
+
help="Filter apps by tag. Format KEY=VALUE. Example --tag foo=bar --tag x=y. If multiple tags are provided, the app must match all of them.",
|
563
|
+
multiple=True,
|
564
|
+
)
|
565
|
+
@click.option(
|
566
|
+
"--format",
|
567
|
+
type=click.Choice(["json", "text"]),
|
568
|
+
help="Format the output",
|
569
|
+
default="text",
|
570
|
+
)
|
571
|
+
@click.option(
|
572
|
+
"--auth-type", type=click.Choice(AuthType.enums()), help="Filter apps by Auth type"
|
573
|
+
)
|
574
|
+
@click.pass_context
|
575
|
+
def list(ctx, project, branch, name, tags, format, auth_type):
|
576
|
+
"""List apps in the Outerbounds Platform."""
|
577
|
+
|
578
|
+
filtered_capsules = list_and_filter_capsules(
|
579
|
+
ctx.obj.api_url, ctx.obj.perimeter, project, branch, name, tags, auth_type, None
|
580
|
+
)
|
581
|
+
if format == "json":
|
582
|
+
click.echo(json.dumps(filtered_capsules, indent=4))
|
583
|
+
else:
|
584
|
+
headers, table_data = _parse_capsule_table(filtered_capsules)
|
585
|
+
print_table(table_data, headers)
|
586
|
+
|
587
|
+
|
588
|
+
@app.command(help="Delete an app/apps from the Outerbounds Platform.")
|
589
|
+
@click.option("--name", type=str, help="Filter app to delete by name")
|
590
|
+
@click.option("--id", "cap_id", type=str, help="Filter app to delete by id")
|
591
|
+
@click.option("--project", type=str, help="Filter apps to delete by project")
|
592
|
+
@click.option("--branch", type=str, help="Filter apps to delete by branch")
|
593
|
+
@click.option(
|
594
|
+
"--tag",
|
595
|
+
"tags",
|
596
|
+
multiple=True,
|
597
|
+
type=KVPairType,
|
598
|
+
help="Filter apps to delete by tag. Format KEY=VALUE. Example --tag foo=bar --tag x=y. If multiple tags are provided, the app must match all of them.",
|
599
|
+
)
|
600
|
+
@click.pass_context
|
601
|
+
def delete(ctx, name, cap_id, project, branch, tags):
|
602
|
+
|
603
|
+
"""Delete an app/apps from the Outerbounds Platform."""
|
604
|
+
# Atleast one of the args need to be provided
|
605
|
+
if not any(
|
606
|
+
[
|
607
|
+
name is not None,
|
608
|
+
cap_id is not None,
|
609
|
+
project is not None,
|
610
|
+
branch is not None,
|
611
|
+
len(tags) != 0,
|
612
|
+
]
|
613
|
+
):
|
614
|
+
raise AppConfigError(
|
615
|
+
"Atleast one of the options need to be provided. You can use --name, --id, --project, --branch, --tag"
|
616
|
+
)
|
617
|
+
|
618
|
+
filtered_capsules = list_and_filter_capsules(
|
619
|
+
ctx.obj.api_url, ctx.obj.perimeter, project, branch, name, tags, None, cap_id
|
620
|
+
)
|
621
|
+
|
622
|
+
headers, table_data = _parse_capsule_table(filtered_capsules)
|
623
|
+
click.secho("The following apps will be deleted:", fg="red", bold=True)
|
624
|
+
print_table(table_data, headers)
|
625
|
+
|
626
|
+
# Confirm the deletion
|
627
|
+
confirm = click.prompt(
|
628
|
+
click.style(
|
629
|
+
"💊 Are you sure you want to delete these apps?", fg="red", bold=True
|
630
|
+
),
|
631
|
+
default="no",
|
632
|
+
type=click.Choice(["yes", "no"]),
|
633
|
+
)
|
634
|
+
if confirm == "no":
|
635
|
+
exit(1)
|
636
|
+
|
637
|
+
def item_show_func(x):
|
638
|
+
if not x:
|
639
|
+
return None
|
640
|
+
name = x.get("spec", {}).get("displayName", "")
|
641
|
+
id = x.get("id", "")
|
642
|
+
return click.style("💊 deleting %s [%s]" % (name, id), fg="red", bold=True)
|
643
|
+
|
644
|
+
with click.progressbar(
|
645
|
+
filtered_capsules,
|
646
|
+
label=click.style("💊 Deleting apps...", fg="red", bold=True),
|
647
|
+
fill_char=click.style("█", fg="red", bold=True),
|
648
|
+
empty_char=click.style("░", fg="red", bold=True),
|
649
|
+
item_show_func=item_show_func,
|
650
|
+
) as bar:
|
651
|
+
for capsule in bar:
|
652
|
+
Capsule.delete(capsule.get("id"), ctx.obj.api_url, ctx.obj.perimeter)
|
653
|
+
|
654
|
+
|
493
655
|
@app.command(help="Run an app locally (for testing).")
|
494
656
|
@common_run_options
|
495
657
|
@click.pass_context
|
outerbounds/apps/capsule.py
CHANGED
@@ -270,9 +270,10 @@ def get_capsule(capsule_id: str, api_url: str, request_headers: dict):
|
|
270
270
|
|
271
271
|
|
272
272
|
def delete_capsule(capsule_id: str, api_url: str, request_headers: dict):
|
273
|
+
_url = os.path.join(api_url, capsule_id)
|
273
274
|
response = safe_requests_wrapper(
|
274
275
|
requests.delete,
|
275
|
-
|
276
|
+
_url,
|
276
277
|
headers=request_headers,
|
277
278
|
retryable_status_codes=[409], # todo : verify me
|
278
279
|
)
|
@@ -281,15 +282,117 @@ def delete_capsule(capsule_id: str, api_url: str, request_headers: dict):
|
|
281
282
|
f"Failed to delete capsule: {response.status_code} {response.text}"
|
282
283
|
)
|
283
284
|
|
285
|
+
if response.status_code == 200:
|
286
|
+
return True
|
287
|
+
return False
|
288
|
+
|
289
|
+
|
290
|
+
def list_capsules(api_url: str, request_headers: dict):
|
291
|
+
response = safe_requests_wrapper(
|
292
|
+
requests.get,
|
293
|
+
api_url,
|
294
|
+
headers=request_headers,
|
295
|
+
)
|
296
|
+
if response.status_code >= 400:
|
297
|
+
raise TODOException(
|
298
|
+
f"Failed to list capsules: {response.status_code} {response.text}"
|
299
|
+
)
|
284
300
|
return response.json()
|
285
301
|
|
286
302
|
|
303
|
+
def list_and_filter_capsules(
|
304
|
+
api_url, perimeter, project, branch, name, tags, auth_type, capsule_id
|
305
|
+
):
|
306
|
+
capsules = Capsule.list(api_url, perimeter)
|
307
|
+
|
308
|
+
def _tags_match(tags, key, value):
|
309
|
+
for t in tags:
|
310
|
+
if t["key"] == key and t["value"] == value:
|
311
|
+
return True
|
312
|
+
return False
|
313
|
+
|
314
|
+
def _all_tags_match(tags, tags_to_match):
|
315
|
+
for t in tags_to_match:
|
316
|
+
if _tags_match(tags, t["key"], t["value"]):
|
317
|
+
return True
|
318
|
+
return False
|
319
|
+
|
320
|
+
def _filter_capsules(capsules, project, branch, name, tags, auth_type, capsule_id):
|
321
|
+
_filtered_capsules = []
|
322
|
+
for capsule in capsules:
|
323
|
+
set_tags = capsule.get("spec", {}).get("tags", [])
|
324
|
+
display_name = capsule.get("spec", {}).get("displayName", None)
|
325
|
+
set_id = capsule.get("id", None)
|
326
|
+
set_auth_type = (
|
327
|
+
capsule.get("spec", {}).get("authConfig", {}).get("authType", None)
|
328
|
+
)
|
329
|
+
|
330
|
+
if auth_type and set_auth_type != auth_type:
|
331
|
+
continue
|
332
|
+
if project and not _tags_match(set_tags, "project", project):
|
333
|
+
continue
|
334
|
+
if branch and not _tags_match(set_tags, "branch", branch):
|
335
|
+
continue
|
336
|
+
if name and display_name != name:
|
337
|
+
continue
|
338
|
+
if tags and not _all_tags_match(set_tags, tags):
|
339
|
+
continue
|
340
|
+
if capsule_id and set_id != capsule_id:
|
341
|
+
continue
|
342
|
+
|
343
|
+
_filtered_capsules.append(capsule)
|
344
|
+
return _filtered_capsules
|
345
|
+
|
346
|
+
return _filter_capsules(
|
347
|
+
capsules, project, branch, name, tags, auth_type, capsule_id
|
348
|
+
)
|
349
|
+
|
350
|
+
|
287
351
|
class Capsule:
|
288
352
|
|
289
353
|
status: CapsuleStateMachine
|
290
354
|
|
291
355
|
identifier = None
|
292
356
|
|
357
|
+
@classmethod
|
358
|
+
def list(cls, base_url: str, perimeter: str):
|
359
|
+
base_url = cls._create_base_url(base_url, perimeter)
|
360
|
+
from metaflow.metaflow_config import SERVICE_HEADERS
|
361
|
+
|
362
|
+
request_headers = {
|
363
|
+
**{"Content-Type": "application/json", "Connection": "keep-alive"},
|
364
|
+
**(SERVICE_HEADERS or {}),
|
365
|
+
}
|
366
|
+
_capsules = list_capsules(base_url, request_headers)
|
367
|
+
if "capsules" not in _capsules:
|
368
|
+
raise TODOException(f"Failed to list capsules")
|
369
|
+
return _capsules.get("capsules", [])
|
370
|
+
|
371
|
+
@classmethod
|
372
|
+
def delete(cls, identifier: str, base_url: str, perimeter: str):
|
373
|
+
base_url = cls._create_base_url(base_url, perimeter)
|
374
|
+
from metaflow.metaflow_config import SERVICE_HEADERS
|
375
|
+
|
376
|
+
request_headers = {
|
377
|
+
**{"Content-Type": "application/json", "Connection": "keep-alive"},
|
378
|
+
**(SERVICE_HEADERS or {}),
|
379
|
+
}
|
380
|
+
return delete_capsule(identifier, base_url, request_headers)
|
381
|
+
|
382
|
+
@classmethod
|
383
|
+
def _create_base_url(
|
384
|
+
cls,
|
385
|
+
base_url: str,
|
386
|
+
perimeter: str,
|
387
|
+
):
|
388
|
+
return os.path.join(
|
389
|
+
base_url,
|
390
|
+
"v1",
|
391
|
+
"perimeters",
|
392
|
+
perimeter,
|
393
|
+
"capsules",
|
394
|
+
)
|
395
|
+
|
293
396
|
# TODO: Current default timeout is very large of 5 minutes. Ideally we should have finished the deployed in less than 1 minutes.
|
294
397
|
def __init__(
|
295
398
|
self,
|
@@ -299,12 +402,9 @@ class Capsule:
|
|
299
402
|
debug_dir: Optional[str] = None,
|
300
403
|
):
|
301
404
|
self._app_config = app_config
|
302
|
-
self._base_url =
|
405
|
+
self._base_url = self._create_base_url(
|
303
406
|
base_url,
|
304
|
-
"v1",
|
305
|
-
"perimeters",
|
306
407
|
app_config.get_state("perimeter"),
|
307
|
-
"capsules",
|
308
408
|
)
|
309
409
|
self._create_timeout = create_timeout
|
310
410
|
self._debug_dir = debug_dir
|
@@ -371,9 +471,3 @@ class Capsule:
|
|
371
471
|
state_machine.check_for_debug(self._debug_dir)
|
372
472
|
|
373
473
|
return capsule_response
|
374
|
-
|
375
|
-
def list(self):
|
376
|
-
return list_capsules(self._base_url, self._request_headers)
|
377
|
-
|
378
|
-
def delete(self):
|
379
|
-
return delete_capsule(self.identifier, self._base_url, self._request_headers)
|
outerbounds/apps/utils.py
CHANGED
@@ -18,6 +18,31 @@ class MaximumRetriesExceeded(Exception):
|
|
18
18
|
return f"Maximum retries exceeded for {self.url}[{self.method}] {self.status_code} {self.text}"
|
19
19
|
|
20
20
|
|
21
|
+
class KeyValueDictPair(click.ParamType):
|
22
|
+
name = "KV-DICT-PAIR"
|
23
|
+
|
24
|
+
def convert(self, value, param, ctx):
|
25
|
+
# Parse a string of the form KEY=VALUE into a dict {KEY: VALUE}
|
26
|
+
if len(value.split("=", 1)) != 2:
|
27
|
+
self.fail(
|
28
|
+
f"Invalid format for {value}. Expected format: KEY=VALUE", param, ctx
|
29
|
+
)
|
30
|
+
|
31
|
+
key, _value = value.split("=", 1)
|
32
|
+
try:
|
33
|
+
return {"key": key, "value": json.loads(_value)}
|
34
|
+
except json.JSONDecodeError:
|
35
|
+
return {"key": key, "value": _value}
|
36
|
+
except Exception as e:
|
37
|
+
self.fail(f"Invalid value for {value}. Error: {e}", param, ctx)
|
38
|
+
|
39
|
+
def __str__(self):
|
40
|
+
return repr(self)
|
41
|
+
|
42
|
+
def __repr__(self):
|
43
|
+
return "KV-PAIR"
|
44
|
+
|
45
|
+
|
21
46
|
class KeyValuePair(click.ParamType):
|
22
47
|
name = "KV-PAIR"
|
23
48
|
|
@@ -155,6 +180,7 @@ KVPairType = KeyValuePair()
|
|
155
180
|
MetaflowArtifactType = MountMetaflowArtifact()
|
156
181
|
SecretMountType = MountSecret()
|
157
182
|
CommaSeparatedListType = CommaSeparatedList()
|
183
|
+
KVDictType = KeyValueDictPair()
|
158
184
|
|
159
185
|
|
160
186
|
class TODOException(Exception):
|
@@ -410,7 +410,7 @@ def kill_process(config_dir=None, profile=None, port=-1, name=""):
|
|
410
410
|
default=os.environ.get("METAFLOW_PROFILE", ""),
|
411
411
|
help="The named metaflow profile in which your workstation exists",
|
412
412
|
)
|
413
|
-
def
|
413
|
+
def list_local(config_dir=None, profile=None):
|
414
414
|
if "WORKSTATION_ID" not in os.environ:
|
415
415
|
click.secho(
|
416
416
|
"All outerbounds app commands can only be run from a workstation.",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: outerbounds
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.176rc2
|
4
4
|
Summary: More Data Science, Less Administration
|
5
5
|
License: Proprietary
|
6
6
|
Keywords: data science,machine learning,MLOps
|
@@ -29,8 +29,8 @@ Requires-Dist: google-cloud-secret-manager (>=2.20.0,<3.0.0) ; extra == "gcp"
|
|
29
29
|
Requires-Dist: google-cloud-storage (>=2.14.0,<3.0.0) ; extra == "gcp"
|
30
30
|
Requires-Dist: metaflow-checkpoint (==0.2.1)
|
31
31
|
Requires-Dist: ob-metaflow (==2.15.14.1)
|
32
|
-
Requires-Dist: ob-metaflow-extensions (==1.1.
|
33
|
-
Requires-Dist: ob-metaflow-stubs (==6.0.3.
|
32
|
+
Requires-Dist: ob-metaflow-extensions (==1.1.163rc3)
|
33
|
+
Requires-Dist: ob-metaflow-stubs (==6.0.3.176rc2)
|
34
34
|
Requires-Dist: opentelemetry-distro (>=0.41b0) ; extra == "otel"
|
35
35
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http (>=1.20.0) ; extra == "otel"
|
36
36
|
Requires-Dist: opentelemetry-instrumentation-requests (>=0.41b0) ; extra == "otel"
|
@@ -40,10 +40,10 @@ outerbounds/_vendor/yaml/scanner.py,sha256=ZcI8IngR56PaQ0m27WU2vxCqmDCuRjz-hr7pi
|
|
40
40
|
outerbounds/_vendor/yaml/serializer.py,sha256=8wFZRy9SsQSktF_f9OOroroqsh4qVUe53ry07P9UgCc,4368
|
41
41
|
outerbounds/_vendor/yaml/tokens.py,sha256=JBSu38wihGr4l73JwbfMA7Ks1-X84g8-NskTz7KwPmA,2578
|
42
42
|
outerbounds/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
43
|
-
outerbounds/apps/app_cli.py,sha256=
|
43
|
+
outerbounds/apps/app_cli.py,sha256=kAKanVOVdBKI82ihAkRL2XcCLYyA-qkr_4FPS9xY-XI,23377
|
44
44
|
outerbounds/apps/app_config.py,sha256=KBmW9grhiuG9XZG-R0GZkM-024cjj6ztGzOX_2wZW34,11291
|
45
45
|
outerbounds/apps/artifacts.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
46
|
-
outerbounds/apps/capsule.py,sha256=
|
46
|
+
outerbounds/apps/capsule.py,sha256=8UC0MXn3SeLBwX8k3ej5Uiqy6U5tcynIrNFZ0GRqFNk,16696
|
47
47
|
outerbounds/apps/cli_to_config.py,sha256=hV6rfPgCiAX03O363GkvdjSIJBt3-oSbL6F2sTUucFE,3195
|
48
48
|
outerbounds/apps/code_package/__init__.py,sha256=8McF7pgx8ghvjRnazp2Qktlxi9yYwNiwESSQrk-2oW8,68
|
49
49
|
outerbounds/apps/code_package/code_packager.py,sha256=SQDBXKwizzpag5GpwoZpvvkyPOodRSQwk2ecAAfO0HI,23316
|
@@ -53,11 +53,11 @@ outerbounds/apps/dependencies.py,sha256=SqvdFQdFZZW0wXX_CHMHCrfE0TwaRkTvGCRbQ2Mx
|
|
53
53
|
outerbounds/apps/deployer.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
54
|
outerbounds/apps/experimental/__init__.py,sha256=12L_FzZyzv162uo4I6cmlrxat7feUtIu_kxbObTJZTA,3059
|
55
55
|
outerbounds/apps/secrets.py,sha256=27qf04lOBqRjvcswj0ldHOmntP2T6SEjtMJtkJQ_GUg,6100
|
56
|
-
outerbounds/apps/utils.py,sha256=
|
56
|
+
outerbounds/apps/utils.py,sha256=xKaSJRmAYToyWvantixLES1vdi5vLLdoQ6yZXAv9IBw,8089
|
57
57
|
outerbounds/apps/validations.py,sha256=AVEw9eCvkzqq1m5ZC8btaWrSR6kWYKzarELfrASuAwQ,1117
|
58
58
|
outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
|
59
59
|
outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
|
60
|
-
outerbounds/command_groups/apps_cli.py,sha256=
|
60
|
+
outerbounds/command_groups/apps_cli.py,sha256=ecXyLhGxjbct62iqviP9qBX8s4d-XG56ICpTM2h2etk,20821
|
61
61
|
outerbounds/command_groups/cli.py,sha256=I8b0zyY_xhZfmaLgJcgR98YXxgirkUK7xsjknEd4nfc,534
|
62
62
|
outerbounds/command_groups/fast_bakery_cli.py,sha256=5kja7v6C651XAY6dsP_IkBPJQgfU4hA4S9yTOiVPhW0,6213
|
63
63
|
outerbounds/command_groups/flowprojects_cli.py,sha256=gFAA_zUIyhD092Hd7IW5InuIxOqdwRJsHgyWQjy8LZw,3792
|
@@ -72,7 +72,7 @@ outerbounds/utils/metaflowconfig.py,sha256=l2vJbgPkLISU-XPGZFaC8ZKmYFyJemlD6bwB-
|
|
72
72
|
outerbounds/utils/schema.py,sha256=lMUr9kNgn9wy-sO_t_Tlxmbt63yLeN4b0xQXbDUDj4A,2331
|
73
73
|
outerbounds/utils/utils.py,sha256=4Z8cszNob_8kDYCLNTrP-wWads_S_MdL3Uj3ju4mEsk,501
|
74
74
|
outerbounds/vendor.py,sha256=gRLRJNXtZBeUpPEog0LOeIsl6GosaFFbCxUvR4bW6IQ,5093
|
75
|
-
outerbounds-0.3.
|
76
|
-
outerbounds-0.3.
|
77
|
-
outerbounds-0.3.
|
78
|
-
outerbounds-0.3.
|
75
|
+
outerbounds-0.3.176rc2.dist-info/METADATA,sha256=sv3uctge_hMpjW2RR9Il9aD6N1d_bIIXfYvnjcdtL9c,1846
|
76
|
+
outerbounds-0.3.176rc2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
77
|
+
outerbounds-0.3.176rc2.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
|
78
|
+
outerbounds-0.3.176rc2.dist-info/RECORD,,
|
File without changes
|
File without changes
|