fal 0.15.2__py3-none-any.whl → 1.0.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.
Potentially problematic release.
This version of fal might be problematic. Click here for more details.
- fal/__init__.py +4 -0
- fal/__main__.py +2 -2
- fal/_fal_version.py +16 -0
- fal/_version.py +6 -0
- fal/api.py +10 -1
- fal/app.py +29 -1
- fal/cli/__init__.py +1 -0
- fal/cli/apps.py +313 -0
- fal/cli/auth.py +59 -0
- fal/cli/debug.py +65 -0
- fal/cli/deploy.py +146 -0
- fal/cli/keys.py +118 -0
- fal/cli/main.py +82 -0
- fal/cli/parser.py +74 -0
- fal/cli/run.py +33 -0
- fal/cli/secrets.py +107 -0
- fal/exceptions/__init__.py +0 -29
- fal/flags.py +0 -1
- fal/sdk.py +1 -0
- fal/utils.py +55 -0
- {fal-0.15.2.dist-info → fal-1.0.0.dist-info}/METADATA +2 -2
- {fal-0.15.2.dist-info → fal-1.0.0.dist-info}/RECORD +25 -14
- fal-1.0.0.dist-info/entry_points.txt +2 -0
- fal/cli.py +0 -622
- fal/exceptions/handlers.py +0 -58
- fal-0.15.2.dist-info/entry_points.txt +0 -2
- {fal-0.15.2.dist-info → fal-1.0.0.dist-info}/WHEEL +0 -0
- {fal-0.15.2.dist-info → fal-1.0.0.dist-info}/top_level.txt +0 -0
fal/__init__.py
CHANGED
|
@@ -7,6 +7,8 @@ from fal.app import App, endpoint, realtime, wrap_app # noqa: F401
|
|
|
7
7
|
from fal.sdk import FalServerlessKeyCredentials
|
|
8
8
|
from fal.sync import sync_dir
|
|
9
9
|
|
|
10
|
+
from ._version import __version__, version_tuple # noqa: F401
|
|
11
|
+
|
|
10
12
|
local = LocalHost()
|
|
11
13
|
serverless = FalServerlessHost()
|
|
12
14
|
|
|
@@ -22,4 +24,6 @@ __all__ = [
|
|
|
22
24
|
# "wrap_app",
|
|
23
25
|
"FalServerlessKeyCredentials",
|
|
24
26
|
"sync_dir",
|
|
27
|
+
"__version__",
|
|
28
|
+
"version_tuple",
|
|
25
29
|
]
|
fal/__main__.py
CHANGED
fal/_fal_version.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '1.0.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (1, 0, 0)
|
fal/_version.py
ADDED
fal/api.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
import os
|
|
4
5
|
import sys
|
|
5
6
|
import threading
|
|
6
7
|
from collections import defaultdict
|
|
@@ -832,6 +833,9 @@ class BaseServable:
|
|
|
832
833
|
"""
|
|
833
834
|
pass
|
|
834
835
|
|
|
836
|
+
def _add_extra_routes(self, app: FastAPI):
|
|
837
|
+
pass
|
|
838
|
+
|
|
835
839
|
@asynccontextmanager
|
|
836
840
|
async def lifespan(self, app: FastAPI):
|
|
837
841
|
yield
|
|
@@ -842,7 +846,10 @@ class BaseServable:
|
|
|
842
846
|
from fastapi.responses import JSONResponse
|
|
843
847
|
from starlette_exporter import PrometheusMiddleware
|
|
844
848
|
|
|
845
|
-
_app = FalFastAPI(
|
|
849
|
+
_app = FalFastAPI(
|
|
850
|
+
lifespan=self.lifespan,
|
|
851
|
+
root_path=os.getenv("FAL_APP_ROOT_PATH") or "",
|
|
852
|
+
)
|
|
846
853
|
|
|
847
854
|
_app.add_middleware(
|
|
848
855
|
CORSMiddleware,
|
|
@@ -893,6 +900,8 @@ class BaseServable:
|
|
|
893
900
|
methods=["POST"],
|
|
894
901
|
)
|
|
895
902
|
|
|
903
|
+
self._add_extra_routes(_app)
|
|
904
|
+
|
|
896
905
|
return _app
|
|
897
906
|
|
|
898
907
|
def openapi(self) -> dict[str, Any]:
|
fal/app.py
CHANGED
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
|
+
import re
|
|
6
7
|
import typing
|
|
7
8
|
from contextlib import asynccontextmanager
|
|
8
9
|
from typing import Any, Callable, ClassVar, TypeVar
|
|
@@ -64,14 +65,33 @@ def wrap_app(cls: type[App], **kwargs) -> fal.api.IsolatedFunction:
|
|
|
64
65
|
return fn
|
|
65
66
|
|
|
66
67
|
|
|
68
|
+
|
|
69
|
+
PART_FINDER_RE = re.compile(r"[A-Z][a-z]*")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _to_fal_app_name(name: str) -> str:
|
|
73
|
+
# Convert MyGoodApp into my-good-app
|
|
74
|
+
return "-".join(part.lower() for part in PART_FINDER_RE.findall(name))
|
|
75
|
+
|
|
76
|
+
|
|
67
77
|
class App(fal.api.BaseServable):
|
|
68
78
|
requirements: ClassVar[list[str]] = []
|
|
69
79
|
machine_type: ClassVar[str] = "S"
|
|
70
|
-
host_kwargs: ClassVar[dict[str, Any]] = {
|
|
80
|
+
host_kwargs: ClassVar[dict[str, Any]] = {
|
|
81
|
+
"_scheduler": "nomad",
|
|
82
|
+
"_scheduler_options": {
|
|
83
|
+
"storage_region": "us-east",
|
|
84
|
+
},
|
|
85
|
+
"resolver": "uv",
|
|
86
|
+
"keep_alive": 60,
|
|
87
|
+
}
|
|
88
|
+
app_name: ClassVar[str]
|
|
71
89
|
|
|
72
90
|
def __init_subclass__(cls, **kwargs):
|
|
91
|
+
app_name = kwargs.pop("name", None) or _to_fal_app_name(cls.__name__)
|
|
73
92
|
parent_settings = getattr(cls, "host_kwargs", {})
|
|
74
93
|
cls.host_kwargs = {**parent_settings, **kwargs}
|
|
94
|
+
cls.app_name = app_name
|
|
75
95
|
|
|
76
96
|
if cls.__init__ is not App.__init__:
|
|
77
97
|
raise ValueError(
|
|
@@ -100,6 +120,9 @@ class App(fal.api.BaseServable):
|
|
|
100
120
|
finally:
|
|
101
121
|
await _call_any_fn(self.teardown)
|
|
102
122
|
|
|
123
|
+
def health(self):
|
|
124
|
+
return {}
|
|
125
|
+
|
|
103
126
|
def setup(self):
|
|
104
127
|
"""Setup the application before serving."""
|
|
105
128
|
|
|
@@ -141,6 +164,11 @@ class App(fal.api.BaseServable):
|
|
|
141
164
|
)
|
|
142
165
|
return response
|
|
143
166
|
|
|
167
|
+
def _add_extra_routes(self, app: FastAPI):
|
|
168
|
+
@app.get("/health")
|
|
169
|
+
def health():
|
|
170
|
+
return self.health()
|
|
171
|
+
|
|
144
172
|
def provide_hints(self) -> list[str]:
|
|
145
173
|
"""Provide hints for routing the application."""
|
|
146
174
|
raise NotImplementedError
|
fal/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .main import main # noqa: F401
|
fal/cli/apps.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from .parser import FalClientParser
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from fal.sdk import AliasInfo, ApplicationInfo
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _apps_table(apps: list["AliasInfo"]):
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
table = Table()
|
|
13
|
+
table.add_column("Name", no_wrap=True)
|
|
14
|
+
table.add_column("Revision")
|
|
15
|
+
table.add_column("Auth")
|
|
16
|
+
table.add_column("Min Concurrency")
|
|
17
|
+
table.add_column("Max Concurrency")
|
|
18
|
+
table.add_column("Max Multiplexing")
|
|
19
|
+
table.add_column("Keep Alive")
|
|
20
|
+
table.add_column("Active Workers")
|
|
21
|
+
|
|
22
|
+
for app in apps:
|
|
23
|
+
table.add_row(
|
|
24
|
+
app.alias,
|
|
25
|
+
app.revision,
|
|
26
|
+
app.auth_mode,
|
|
27
|
+
str(app.min_concurrency),
|
|
28
|
+
str(app.max_concurrency),
|
|
29
|
+
str(app.max_multiplexing),
|
|
30
|
+
str(app.keep_alive),
|
|
31
|
+
str(app.active_runners),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return table
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _list(args):
|
|
38
|
+
from fal.sdk import FalServerlessClient
|
|
39
|
+
|
|
40
|
+
client = FalServerlessClient(args.host)
|
|
41
|
+
with client.connect() as connection:
|
|
42
|
+
apps = connection.list_aliases()
|
|
43
|
+
table = _apps_table(apps)
|
|
44
|
+
|
|
45
|
+
args.console.print(table)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _add_list_parser(subparsers, parents):
|
|
49
|
+
list_help = "List applications."
|
|
50
|
+
parser = subparsers.add_parser(
|
|
51
|
+
"list",
|
|
52
|
+
description=list_help,
|
|
53
|
+
help=list_help,
|
|
54
|
+
parents=parents,
|
|
55
|
+
)
|
|
56
|
+
parser.set_defaults(func=_list)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _app_rev_table(revs: list["ApplicationInfo"]):
|
|
60
|
+
from rich.table import Table
|
|
61
|
+
|
|
62
|
+
table = Table()
|
|
63
|
+
table.add_column("Revision", no_wrap=True)
|
|
64
|
+
table.add_column("Min Concurrency")
|
|
65
|
+
table.add_column("Max Concurrency")
|
|
66
|
+
table.add_column("Max Multiplexing")
|
|
67
|
+
table.add_column("Keep Alive")
|
|
68
|
+
table.add_column("Active Workers")
|
|
69
|
+
|
|
70
|
+
for rev in revs:
|
|
71
|
+
table.add_row(
|
|
72
|
+
rev.application_id,
|
|
73
|
+
str(rev.min_concurrency),
|
|
74
|
+
str(rev.max_concurrency),
|
|
75
|
+
str(rev.max_multiplexing),
|
|
76
|
+
str(rev.keep_alive),
|
|
77
|
+
str(rev.active_runners),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return table
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _list_rev(args):
|
|
84
|
+
from fal.sdk import FalServerlessClient
|
|
85
|
+
|
|
86
|
+
client = FalServerlessClient(args.host)
|
|
87
|
+
with client.connect() as connection:
|
|
88
|
+
revs = connection.list_applications()
|
|
89
|
+
table = _app_rev_table(revs)
|
|
90
|
+
|
|
91
|
+
args.console.print(table)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _add_list_rev_parser(subparsers, parents):
|
|
95
|
+
list_help = "List application revisions."
|
|
96
|
+
parser = subparsers.add_parser(
|
|
97
|
+
"list-rev",
|
|
98
|
+
description=list_help,
|
|
99
|
+
help=list_help,
|
|
100
|
+
parents=parents,
|
|
101
|
+
)
|
|
102
|
+
parser.set_defaults(func=_list_rev)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _scale(args):
|
|
106
|
+
from fal.sdk import FalServerlessClient
|
|
107
|
+
|
|
108
|
+
client = FalServerlessClient(args.host)
|
|
109
|
+
with client.connect() as connection:
|
|
110
|
+
if (
|
|
111
|
+
args.keep_alive is None
|
|
112
|
+
and args.max_multiplexing is None
|
|
113
|
+
and args.max_concurrency is None
|
|
114
|
+
and args.min_concurrency is None
|
|
115
|
+
):
|
|
116
|
+
args.console.log("No parameters for update were provided, ignoring.")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
alias_info = connection.update_application(
|
|
120
|
+
application_name=args.app_alias,
|
|
121
|
+
keep_alive=args.keep_alive,
|
|
122
|
+
max_multiplexing=args.max_multiplexing,
|
|
123
|
+
max_concurrency=args.max_concurrency,
|
|
124
|
+
min_concurrency=args.min_concurrency,
|
|
125
|
+
)
|
|
126
|
+
table = _apps_table([alias_info])
|
|
127
|
+
|
|
128
|
+
args.console.print(table)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _add_scale_parser(subparsers, parents):
|
|
132
|
+
scale_help = "Scale application."
|
|
133
|
+
parser = subparsers.add_parser(
|
|
134
|
+
"scale",
|
|
135
|
+
description=scale_help,
|
|
136
|
+
help=scale_help,
|
|
137
|
+
parents=parents,
|
|
138
|
+
)
|
|
139
|
+
parser.add_argument(
|
|
140
|
+
"--keep-alive",
|
|
141
|
+
type=int,
|
|
142
|
+
help="Keep alive (seconds).",
|
|
143
|
+
)
|
|
144
|
+
parser.add_argument(
|
|
145
|
+
"--max-multiplexing",
|
|
146
|
+
type=int,
|
|
147
|
+
help="Maximum multiplexing",
|
|
148
|
+
)
|
|
149
|
+
parser.add_argument(
|
|
150
|
+
"--max-concurrency",
|
|
151
|
+
type=int,
|
|
152
|
+
help="Maximum concurrency.",
|
|
153
|
+
)
|
|
154
|
+
parser.add_argument(
|
|
155
|
+
"--min-concurrency",
|
|
156
|
+
type=int,
|
|
157
|
+
help="Minimum concurrency",
|
|
158
|
+
)
|
|
159
|
+
parser.set_defaults(func=_scale)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _set_rev(args):
|
|
163
|
+
from fal.sdk import FalServerlessClient
|
|
164
|
+
|
|
165
|
+
client = FalServerlessClient(args.host)
|
|
166
|
+
with client.connect() as connection:
|
|
167
|
+
connection.create_alias(args.app_name, args.app_rev, args.auth)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _add_set_rev_parser(subparsers, parents):
|
|
171
|
+
from fal.sdk import ALIAS_AUTH_MODES
|
|
172
|
+
|
|
173
|
+
set_help = "Set application to a particular revision."
|
|
174
|
+
parser = subparsers.add_parser(
|
|
175
|
+
"set-rev",
|
|
176
|
+
description=set_help,
|
|
177
|
+
help=set_help,
|
|
178
|
+
parents=parents,
|
|
179
|
+
)
|
|
180
|
+
parser.add_argument(
|
|
181
|
+
"app_name",
|
|
182
|
+
help="Application name.",
|
|
183
|
+
)
|
|
184
|
+
parser.add_argument(
|
|
185
|
+
"app_rev",
|
|
186
|
+
help="Application revision.",
|
|
187
|
+
)
|
|
188
|
+
parser.add_argument(
|
|
189
|
+
"--auth",
|
|
190
|
+
choices=ALIAS_AUTH_MODES,
|
|
191
|
+
default="private",
|
|
192
|
+
help="Application authentication mode.",
|
|
193
|
+
)
|
|
194
|
+
parser.set_defaults(func=_set_rev)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _runners(args):
|
|
198
|
+
from rich.table import Table
|
|
199
|
+
|
|
200
|
+
from fal.sdk import FalServerlessClient
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
client = FalServerlessClient(args.host)
|
|
204
|
+
with client.connect() as connection:
|
|
205
|
+
runners = connection.list_alias_runners(alias=args.app_name)
|
|
206
|
+
|
|
207
|
+
table = Table()
|
|
208
|
+
table.add_column("Runner ID")
|
|
209
|
+
table.add_column("In Flight Requests")
|
|
210
|
+
table.add_column("Expires in")
|
|
211
|
+
table.add_column("Uptime")
|
|
212
|
+
|
|
213
|
+
for runner in runners:
|
|
214
|
+
table.add_row(
|
|
215
|
+
runner.runner_id,
|
|
216
|
+
str(runner.in_flight_requests),
|
|
217
|
+
(
|
|
218
|
+
"N/A (active)"
|
|
219
|
+
if not runner.expiration_countdown
|
|
220
|
+
else f"{runner.expiration_countdown}s"
|
|
221
|
+
),
|
|
222
|
+
f"{runner.uptime} ({runner.uptime.total_seconds()}s)",
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
args.console.print(table)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _add_runners_parser(subparsers, parents):
|
|
229
|
+
runners_help = "List application runners."
|
|
230
|
+
parser = subparsers.add_parser(
|
|
231
|
+
"runners",
|
|
232
|
+
description=runners_help,
|
|
233
|
+
help=runners_help,
|
|
234
|
+
parents=parents,
|
|
235
|
+
)
|
|
236
|
+
parser.add_argument(
|
|
237
|
+
"app_name",
|
|
238
|
+
help="Application name.",
|
|
239
|
+
)
|
|
240
|
+
parser.set_defaults(func=_runners)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _delete(args):
|
|
244
|
+
from fal.sdk import FalServerlessClient
|
|
245
|
+
|
|
246
|
+
client = FalServerlessClient(args.host)
|
|
247
|
+
with client.connect() as connection:
|
|
248
|
+
connection.delete_alias(args.app_name)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _add_delete_parser(subparsers, parents):
|
|
252
|
+
delete_help = "Delete application."
|
|
253
|
+
parser = subparsers.add_parser(
|
|
254
|
+
"delete",
|
|
255
|
+
description=delete_help,
|
|
256
|
+
help=delete_help,
|
|
257
|
+
parents=parents,
|
|
258
|
+
)
|
|
259
|
+
parser.add_argument(
|
|
260
|
+
"app_name",
|
|
261
|
+
help="Application name.",
|
|
262
|
+
)
|
|
263
|
+
parser.set_defaults(func=_delete)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _delete_rev(args):
|
|
267
|
+
from fal.sdk import FalServerlessClient
|
|
268
|
+
|
|
269
|
+
client = FalServerlessClient(args.host)
|
|
270
|
+
with client.connect() as connection:
|
|
271
|
+
connection.delete_application(args.app_rev)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _add_delete_rev_parser(subparsers, parents):
|
|
275
|
+
delete_help = "Delete application revision."
|
|
276
|
+
parser = subparsers.add_parser(
|
|
277
|
+
"delete-rev",
|
|
278
|
+
description=delete_help,
|
|
279
|
+
help=delete_help,
|
|
280
|
+
parents=parents,
|
|
281
|
+
)
|
|
282
|
+
parser.add_argument(
|
|
283
|
+
"app_rev",
|
|
284
|
+
help="Application revision.",
|
|
285
|
+
)
|
|
286
|
+
parser.set_defaults(func=_delete_rev)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def add_parser(main_subparsers, parents):
|
|
290
|
+
apps_help = "Manage fal applications."
|
|
291
|
+
parser = main_subparsers.add_parser(
|
|
292
|
+
"apps",
|
|
293
|
+
aliases=["app"],
|
|
294
|
+
description=apps_help,
|
|
295
|
+
help=apps_help,
|
|
296
|
+
parents=parents,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
subparsers = parser.add_subparsers(
|
|
300
|
+
title="Commands",
|
|
301
|
+
metavar="command",
|
|
302
|
+
required=True,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
parents = [*parents, FalClientParser(add_help=False)]
|
|
306
|
+
|
|
307
|
+
_add_list_parser(subparsers, parents)
|
|
308
|
+
_add_list_rev_parser(subparsers, parents)
|
|
309
|
+
_add_set_rev_parser(subparsers, parents)
|
|
310
|
+
_add_scale_parser(subparsers, parents)
|
|
311
|
+
_add_runners_parser(subparsers, parents)
|
|
312
|
+
_add_delete_parser(subparsers, parents)
|
|
313
|
+
_add_delete_rev_parser(subparsers, parents)
|
fal/cli/auth.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from fal.auth import USER, login, logout
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _login(args):
|
|
5
|
+
login()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _logout(args):
|
|
9
|
+
logout()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _whoami(args):
|
|
13
|
+
user_name = USER.info["name"]
|
|
14
|
+
sub = USER.info["sub"]
|
|
15
|
+
args.console.print(f"Hello, {user_name} - '{sub}'")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_parser(main_subparsers, parents):
|
|
19
|
+
auth_help = "Authenticate with fal."
|
|
20
|
+
parser = main_subparsers.add_parser(
|
|
21
|
+
"auth",
|
|
22
|
+
description=auth_help,
|
|
23
|
+
help=auth_help,
|
|
24
|
+
parents=parents,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
subparsers = parser.add_subparsers(
|
|
28
|
+
title="Commands",
|
|
29
|
+
metavar="command",
|
|
30
|
+
dest="cmd",
|
|
31
|
+
required=True,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
login_help = "Log in a user."
|
|
35
|
+
login_parser = subparsers.add_parser(
|
|
36
|
+
"login",
|
|
37
|
+
description=login_help,
|
|
38
|
+
help=login_help,
|
|
39
|
+
parents=parents,
|
|
40
|
+
)
|
|
41
|
+
login_parser.set_defaults(func=_login)
|
|
42
|
+
|
|
43
|
+
logout_help = "Log out the currently logged-in user."
|
|
44
|
+
logout_parser = subparsers.add_parser(
|
|
45
|
+
"logout",
|
|
46
|
+
description=logout_help,
|
|
47
|
+
help=logout_help,
|
|
48
|
+
parents=parents,
|
|
49
|
+
)
|
|
50
|
+
logout_parser.set_defaults(func=_logout)
|
|
51
|
+
|
|
52
|
+
whoami_help = "Show the currently authenticated user."
|
|
53
|
+
whoami_parser = subparsers.add_parser(
|
|
54
|
+
"whoami",
|
|
55
|
+
description=whoami_help,
|
|
56
|
+
help=whoami_help,
|
|
57
|
+
parents=parents,
|
|
58
|
+
)
|
|
59
|
+
whoami_parser.set_defaults(func=_whoami)
|
fal/cli/debug.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from contextlib import ExitStack, contextmanager
|
|
2
|
+
|
|
3
|
+
from .parser import FalParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@contextmanager
|
|
7
|
+
def _pdb():
|
|
8
|
+
try:
|
|
9
|
+
yield
|
|
10
|
+
except Exception:
|
|
11
|
+
try:
|
|
12
|
+
import ipdb as pdb # noqa: T100
|
|
13
|
+
except ImportError:
|
|
14
|
+
import pdb # noqa: T100
|
|
15
|
+
pdb.post_mortem()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@contextmanager
|
|
19
|
+
def _cprofile():
|
|
20
|
+
import cProfile
|
|
21
|
+
|
|
22
|
+
prof = cProfile.Profile()
|
|
23
|
+
prof.enable()
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
yield
|
|
27
|
+
finally:
|
|
28
|
+
prof.disable()
|
|
29
|
+
prof.print_stats(sort="cumtime")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def debugtools(args):
|
|
34
|
+
with ExitStack() as stack:
|
|
35
|
+
if args.pdb:
|
|
36
|
+
stack.enter_context(_pdb())
|
|
37
|
+
if args.cprofile:
|
|
38
|
+
stack.enter_context(_cprofile())
|
|
39
|
+
try:
|
|
40
|
+
yield
|
|
41
|
+
except Exception:
|
|
42
|
+
if args.debug:
|
|
43
|
+
args.console.print_exception()
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_debug_parser():
|
|
48
|
+
parser = FalParser(add_help=False)
|
|
49
|
+
group = parser.add_argument_group(title="Debug")
|
|
50
|
+
group.add_argument(
|
|
51
|
+
"--debug",
|
|
52
|
+
action="store_true",
|
|
53
|
+
help="Show verbose errors."
|
|
54
|
+
)
|
|
55
|
+
group.add_argument(
|
|
56
|
+
"--pdb",
|
|
57
|
+
action="store_true",
|
|
58
|
+
help="Start pdb on error.",
|
|
59
|
+
)
|
|
60
|
+
group.add_argument(
|
|
61
|
+
"--cprofile",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help="Show cProfile report.",
|
|
64
|
+
)
|
|
65
|
+
return parser
|