outerbounds 0.3.133__py3-none-any.whl → 0.3.134rc0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,6 +4,8 @@ from outerbounds._vendor import click
4
4
  import requests
5
5
  import time
6
6
  import random
7
+ import shutil
8
+ import subprocess
7
9
 
8
10
  from ..utils import metaflowconfig
9
11
 
@@ -113,7 +115,12 @@ def start(config_dir=None, profile=None, port=-1, name=""):
113
115
  err=True,
114
116
  )
115
117
  click.secho(
116
- f"App URL: {api_url.replace('api', 'ui')}/apps/{workstation_id}/{name}/",
118
+ f"Browser URL: {api_url.replace('api', 'ui')}/apps/{workstation_id}/{name}/",
119
+ fg="green",
120
+ err=True,
121
+ )
122
+ click.secho(
123
+ f"App URL: {api_url}/apps/{workstation_id}/{name}/",
117
124
  fg="green",
118
125
  err=True,
119
126
  )
@@ -145,7 +152,12 @@ def start(config_dir=None, profile=None, port=-1, name=""):
145
152
  err=True,
146
153
  )
147
154
  click.secho(
148
- f"App URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{name}/",
155
+ f"Browser URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{name}/",
156
+ fg="green",
157
+ err=True,
158
+ )
159
+ click.secho(
160
+ f"App URL: {api_url}/apps/{os.environ['WORKSTATION_ID']}/{name}/",
149
161
  fg="green",
150
162
  err=True,
151
163
  )
@@ -301,6 +313,93 @@ def stop(config_dir=None, profile=None, port=-1, name=""):
301
313
  click.secho(f"Failed to stop app on port {port}!", fg="red", err=True)
302
314
 
303
315
 
316
+ @app.command(help="Kill the process associated with an app.")
317
+ @click.option(
318
+ "-d",
319
+ "--config-dir",
320
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
321
+ help="Path to Metaflow configuration directory",
322
+ show_default=True,
323
+ )
324
+ @click.option(
325
+ "-p",
326
+ "--profile",
327
+ default=os.environ.get("METAFLOW_PROFILE", ""),
328
+ help="The named metaflow profile in which your workstation exists",
329
+ )
330
+ @click.option(
331
+ "--name",
332
+ required=False,
333
+ help="Name of your app",
334
+ default="",
335
+ type=str,
336
+ )
337
+ def kill_process(config_dir=None, profile=None, port=-1, name=""):
338
+ if port > 0 and name:
339
+ click.secho(
340
+ "Please provide either a port number or a name to stop the app, not both.",
341
+ fg="red",
342
+ err=True,
343
+ )
344
+ return
345
+
346
+ if "WORKSTATION_ID" not in os.environ:
347
+ click.secho(
348
+ "All outerbounds app commands can only be run from a workstation.",
349
+ fg="red",
350
+ err=True,
351
+ )
352
+
353
+ return
354
+
355
+ supervisorctl_exists = shutil.which("supervisorctl")
356
+ if not supervisorctl_exists:
357
+ click.secho(
358
+ "This workstation does not support automated app deployment and management. Pleasr reach out to outerbounds support!",
359
+ fg="red",
360
+ err=True,
361
+ )
362
+ return
363
+
364
+ supervisorctl_command = ["supervisorctl", "stop", name]
365
+
366
+ # This is somewhat ugly way of doing this, but there aren't any better ways.
367
+ # Since we use supervisorctl to manage our apps, we have to rely on external calls to
368
+ # kill the process.
369
+ result = subprocess.run(supervisorctl_command, capture_output=True, text=True)
370
+ if result.returncode == 0:
371
+ # It gets uglier.
372
+ # When the process is not running, which is likely due to a user error, the output looks like:
373
+ # myapp: ERROR (not running)
374
+ # but the return code is still 0.
375
+ # If the stop is successful, the output looks like:
376
+ # myapp: stopped
377
+
378
+ if result.stdout.startswith(f"{name}: ERROR (not running)"):
379
+ click.secho(
380
+ f"Process {name} is not in a running state to kill!",
381
+ fg="yellow",
382
+ err=True,
383
+ )
384
+ return
385
+ elif result.stdout.startswith(f"{name}: stopped"):
386
+ click.secho(f"Process {name} killed successfully!", fg="green", err=True)
387
+ return
388
+ else:
389
+ click.secho(f"Process {name} stopped!", fg="red", err=True)
390
+ return
391
+ elif result.returncode == 1:
392
+ # One of the cases where this can happen is if the app name itself is wrong.
393
+ # In this case, the output looks like:
394
+ # 'mya111pp: ERROR (no such process)\n'
395
+ if result.stdout.startswith(f"{name}: ERROR (no such process)"):
396
+ click.secho(f"Process {name} does not exist!", fg="red", err=True)
397
+ return
398
+ else:
399
+ click.secho(f"Failed to kill process {name}!", fg="red", err=True)
400
+ return
401
+
402
+
304
403
  @app.command(help="List all apps on the workstation")
305
404
  @click.option(
306
405
  "-d",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: outerbounds
3
- Version: 0.3.133
3
+ Version: 0.3.134rc0
4
4
  Summary: More Data Science, Less Administration
5
5
  License: Proprietary
6
6
  Keywords: data science,machine learning,MLOps
@@ -14,7 +14,6 @@ Classifier: Programming Language :: Python :: 3.8
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
17
  Provides-Extra: azure
19
18
  Provides-Extra: gcp
20
19
  Provides-Extra: snowflake
@@ -27,9 +26,9 @@ Requires-Dist: google-auth (>=2.27.0,<3.0.0) ; extra == "gcp"
27
26
  Requires-Dist: google-cloud-secret-manager (>=2.20.0,<3.0.0) ; extra == "gcp"
28
27
  Requires-Dist: google-cloud-storage (>=2.14.0,<3.0.0) ; extra == "gcp"
29
28
  Requires-Dist: metaflow-checkpoint (==0.1.6)
30
- Requires-Dist: ob-metaflow (==2.13.4.1)
31
- Requires-Dist: ob-metaflow-extensions (==1.1.121)
32
- Requires-Dist: ob-metaflow-stubs (==6.0.3.133)
29
+ Requires-Dist: ob-metaflow (==2.13.7.1)
30
+ Requires-Dist: ob-metaflow-extensions (==1.1.123rc0)
31
+ Requires-Dist: ob-metaflow-stubs (==6.0.3.134)
33
32
  Requires-Dist: opentelemetry-distro (==0.41b0)
34
33
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.20.0)
35
34
  Requires-Dist: opentelemetry-instrumentation-requests (==0.41b0)
@@ -41,7 +41,7 @@ outerbounds/_vendor/yaml/serializer.py,sha256=8wFZRy9SsQSktF_f9OOroroqsh4qVUe53r
41
41
  outerbounds/_vendor/yaml/tokens.py,sha256=JBSu38wihGr4l73JwbfMA7Ks1-X84g8-NskTz7KwPmA,2578
42
42
  outerbounds/cli_main.py,sha256=e9UMnPysmc7gbrimq2I4KfltggyU7pw59Cn9aEguVcU,74
43
43
  outerbounds/command_groups/__init__.py,sha256=QPWtj5wDRTINDxVUL7XPqG3HoxHNvYOg08EnuSZB2Hc,21
44
- outerbounds/command_groups/apps_cli.py,sha256=8jmQufa0bK2sfRfs7DiWjoJ1oWiqZAixsL4Dte_KY4Y,17201
44
+ outerbounds/command_groups/apps_cli.py,sha256=v9OlQ1b4BGB-cBZiHB6W5gDocDoMmrQ7zdK11QiJ-B8,20847
45
45
  outerbounds/command_groups/cli.py,sha256=q0hdJO4biD3iEOdyJcxnRkeleA8AKAhx842kQ49I6kk,365
46
46
  outerbounds/command_groups/local_setup_cli.py,sha256=tuuqJRXQ_guEwOuQSIf9wkUU0yg8yAs31myGViAK15s,36364
47
47
  outerbounds/command_groups/perimeters_cli.py,sha256=iF_Uw7ROiSctf6FgoJEy30iDBLVE1j9FKuR3shgJRmc,19050
@@ -53,7 +53,7 @@ outerbounds/utils/metaflowconfig.py,sha256=l2vJbgPkLISU-XPGZFaC8ZKmYFyJemlD6bwB-
53
53
  outerbounds/utils/schema.py,sha256=lMUr9kNgn9wy-sO_t_Tlxmbt63yLeN4b0xQXbDUDj4A,2331
54
54
  outerbounds/utils/utils.py,sha256=4Z8cszNob_8kDYCLNTrP-wWads_S_MdL3Uj3ju4mEsk,501
55
55
  outerbounds/vendor.py,sha256=gRLRJNXtZBeUpPEog0LOeIsl6GosaFFbCxUvR4bW6IQ,5093
56
- outerbounds-0.3.133.dist-info/METADATA,sha256=q0HvciFfYuFHoyzBF7-2hnU7EkQIk47mtcxUOxtuWKs,1761
57
- outerbounds-0.3.133.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
58
- outerbounds-0.3.133.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
59
- outerbounds-0.3.133.dist-info/RECORD,,
56
+ outerbounds-0.3.134rc0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
57
+ outerbounds-0.3.134rc0.dist-info/entry_points.txt,sha256=7ye0281PKlvqxu15rjw60zKg2pMsXI49_A8BmGqIqBw,47
58
+ outerbounds-0.3.134rc0.dist-info/METADATA,sha256=Qc7ZAyw3cu9nZF1jrqw8CkvW3y1q8DEvQlzqSM2urvA,1716
59
+ outerbounds-0.3.134rc0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.4.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any