fal 1.11.5__py3-none-any.whl → 1.12.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/_fal_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.11.5'
21
- __version_tuple__ = version_tuple = (1, 11, 5)
20
+ __version__ = version = '1.12.0'
21
+ __version_tuple__ = version_tuple = (1, 12, 0)
fal/auth/__init__.py CHANGED
@@ -142,25 +142,23 @@ def _fetch_teams(bearer_token: str) -> list[dict]:
142
142
  )
143
143
  try:
144
144
  with urlopen(request) as response:
145
- teams = json.load(response)
145
+ return json.load(response)
146
146
  except HTTPError as exc:
147
147
  raise FalServerlessException("Failed to fetch teams") from exc
148
148
 
149
- return [team for team in teams if not team["is_personal"]]
150
-
151
149
 
152
150
  @dataclass
153
151
  class UserAccess:
154
152
  _access_token: str | None = field(repr=False, default=None)
155
153
  _user_info: dict | None = field(repr=False, default=None)
156
154
  _exc: Exception | None = field(repr=False, default=None)
157
- _teams: list[dict] | None = field(repr=False, default=None)
155
+ _accounts: list[dict] | None = field(repr=False, default=None)
158
156
 
159
157
  def invalidate(self) -> None:
160
158
  self._access_token = None
161
159
  self._user_info = None
162
160
  self._exc = None
163
- self._teams = None
161
+ self._accounts = None
164
162
 
165
163
  @property
166
164
  def info(self) -> dict:
@@ -191,13 +189,17 @@ class UserAccess:
191
189
  return "Bearer " + self.access_token
192
190
 
193
191
  @property
194
- def teams(self) -> list[dict]:
195
- if self._teams is None:
196
- self._teams = _fetch_teams(self.bearer_token)
197
- return self._teams
192
+ def accounts(self) -> list[dict]:
193
+ if self._accounts is None:
194
+ self._accounts = _fetch_teams(self.bearer_token)
195
+ self._accounts = sorted(
196
+ self._accounts, key=lambda x: (not x["is_personal"], x["nickname"])
197
+ )
198
+
199
+ return self._accounts
198
200
 
199
- def get_team(self, team: str) -> dict:
200
- for t in self.teams:
201
+ def get_account(self, team: str) -> dict:
202
+ for t in self.accounts:
201
203
  if t["nickname"].lower() == team.lower():
202
204
  return t
203
205
  raise ValueError(f"Team {team} not found")
fal/cli/auth.py CHANGED
@@ -3,44 +3,13 @@ from fal.auth import USER, login, logout
3
3
 
4
4
  def _login(args):
5
5
  from fal.config import Config
6
- from fal.console.prompt import prompt
7
6
 
8
7
  login()
9
8
 
10
- teams = [team["nickname"].lower() for team in USER.teams]
11
- if not teams:
12
- return
13
-
14
- args.console.print("")
15
- args.console.print(
16
- f"You ({USER.info['name']}) are a member of the following teams:\n",
17
- )
18
- for idx, team in enumerate(USER.teams):
19
- args.console.print(f" {idx + 1}. {team['nickname']}")
20
- args.console.print("")
21
-
22
- team_choice = prompt(
23
- args.console,
24
- "Pick a team account to use (leave blank for personal account)",
25
- choices=teams,
26
- show_choices=False,
27
- default=None,
28
- )
29
- args.console.print("")
30
-
31
9
  with Config().edit() as config:
32
- if team_choice:
33
- args.console.print(
34
- f"Setting team to [cyan]{team}[/]. "
35
- "You can change this later with [bold]fal team set[/]."
36
- )
37
- config.set("team", team)
38
- else:
39
- args.console.print(
40
- "Using your personal account. "
41
- "You can change this later with [bold]fal team set[/]."
42
- )
43
- config.unset("team")
10
+ config.unset("team")
11
+
12
+ _set_account(args)
44
13
 
45
14
 
46
15
  def _logout(args):
@@ -51,10 +20,85 @@ def _logout(args):
51
20
  config.unset("team")
52
21
 
53
22
 
23
+ def _list_accounts(args):
24
+ from rich.style import Style
25
+ from rich.table import Table
26
+
27
+ from fal.config import Config
28
+
29
+ config = Config()
30
+ current_account = config.get("team") or USER.info["nickname"]
31
+
32
+ table = Table(border_style=Style(frame=False), show_header=False)
33
+ table.add_column("#")
34
+ table.add_column("Nickname")
35
+ table.add_column("Type")
36
+
37
+ for idx, account in enumerate(USER.accounts):
38
+ selected = account["nickname"] == current_account
39
+ color = "bold yellow" if selected else None
40
+
41
+ table.add_row(
42
+ f"* {idx + 1}" if selected else f" {idx + 1}",
43
+ account["nickname"],
44
+ "Personal" if account["is_personal"] else "Team",
45
+ style=color,
46
+ )
47
+
48
+ args.console.print(table)
49
+
50
+
51
+ def _set_account(args):
52
+ from rich.prompt import Prompt
53
+
54
+ from fal.config import Config
55
+
56
+ if hasattr(args, "account") and args.account:
57
+ if args.account.isdigit():
58
+ acc_index = int(args.account) - 1
59
+ account = USER.accounts[acc_index]
60
+ else:
61
+ account = USER.get_account(args.account)
62
+ else:
63
+ _list_accounts(args)
64
+ indices = list(map(str, range(1, len(USER.accounts) + 1)))
65
+ team_names = [account["nickname"] for account in USER.accounts]
66
+ acc_choice = Prompt.ask(
67
+ "Select an account by number",
68
+ choices=indices + team_names,
69
+ show_choices=False,
70
+ )
71
+ if acc_choice in indices:
72
+ acc_index = int(acc_choice) - 1
73
+ account = USER.accounts[acc_index]
74
+ else:
75
+ account = USER.get_account(acc_choice)
76
+
77
+ if account["is_personal"]:
78
+ args.console.print(f"Using personal account {account['nickname']}")
79
+ else:
80
+ args.console.print(f"Using team account {account['nickname']}")
81
+
82
+ with Config().edit() as config:
83
+ config.set("team", account["nickname"])
84
+
85
+
54
86
  def _whoami(args):
55
- user_name = USER.info["name"]
56
- sub = USER.info["sub"]
57
- args.console.print(f"Hello, {user_name} - '{sub}'")
87
+ from fal.config import Config
88
+
89
+ config = Config()
90
+
91
+ team = config.get("team")
92
+ if team:
93
+ account = USER.get_account(team)
94
+ else:
95
+ account = USER.get_account(USER.info["nickname"])
96
+
97
+ nickname = account["nickname"]
98
+ full_name = account["full_name"]
99
+ user_id = account["user_id"]
100
+
101
+ args.console.print(f"Hello, {full_name}: {nickname!r} - {user_id!r}")
58
102
 
59
103
 
60
104
  def add_parser(main_subparsers, parents):
fal/cli/teams.py CHANGED
@@ -1,39 +1,4 @@
1
- def _list(args):
2
- from rich.table import Table
3
-
4
- from fal.auth import USER
5
- from fal.config import Config
6
-
7
- table = Table()
8
- table.add_column("Default")
9
- table.add_column("Team")
10
- table.add_column("Full Name")
11
- table.add_column("ID")
12
-
13
- default_team = Config().get("team")
14
-
15
- for team in USER.teams:
16
- default = default_team and default_team.lower() == team["nickname"].lower()
17
- table.add_row(
18
- "*" if default else "", team["nickname"], team["full_name"], team["user_id"]
19
- )
20
-
21
- args.console.print(table)
22
-
23
-
24
- def _set(args):
25
- from fal.config import Config
26
- from fal.sdk import USER
27
-
28
- team = args.team.lower()
29
- for team_info in USER.teams:
30
- if team_info["nickname"].lower() == team:
31
- break
32
- else:
33
- raise ValueError(f"Team {args.team} not found")
34
-
35
- with Config().edit() as config:
36
- config.set("team", team)
1
+ from fal.cli.auth import _list_accounts, _set_account
37
2
 
38
3
 
39
4
  def _unset(args):
@@ -67,7 +32,7 @@ def add_parser(main_subparsers, parents):
67
32
  help=list_help,
68
33
  parents=parents,
69
34
  )
70
- list_parser.set_defaults(func=_list)
35
+ list_parser.set_defaults(func=_list_accounts)
71
36
 
72
37
  set_help = "Set the current team."
73
38
  set_parser = subparsers.add_parser(
@@ -76,8 +41,8 @@ def add_parser(main_subparsers, parents):
76
41
  help=set_help,
77
42
  parents=parents,
78
43
  )
79
- set_parser.add_argument("team", help="The team to set.")
80
- set_parser.set_defaults(func=_set)
44
+ set_parser.add_argument("account", help="The team to set.")
45
+ set_parser.set_defaults(func=_set_account)
81
46
 
82
47
  unset_help = "Unset the current team."
83
48
  unset_parser = subparsers.add_parser(
fal/sdk.py CHANGED
@@ -183,7 +183,7 @@ def get_default_credentials(team: str | None = None) -> Credentials:
183
183
  else:
184
184
  config = Config()
185
185
  team = team or config.get("team")
186
- team_id = USER.get_team(team)["user_id"] if team else None
186
+ team_id = USER.get_account(team)["user_id"] if team else None
187
187
  return AuthenticatedCredentials(team_id=team_id)
188
188
 
189
189
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fal
3
- Version: 1.11.5
3
+ Version: 1.12.0
4
4
  Summary: fal is an easy-to-use Serverless Python Framework
5
5
  Author: Features & Labels <support@fal.ai>
6
6
  Requires-Python: >=3.8
@@ -1,6 +1,6 @@
1
1
  fal/__init__.py,sha256=wXs1G0gSc7ZK60-bHe-B2m0l_sA6TrFk4BxY0tMoLe8,784
2
2
  fal/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83
3
- fal/_fal_version.py,sha256=C9jc23h2VfZSE0orSfoOaBMqebv8jMlmnPO1wUWfXak,513
3
+ fal/_fal_version.py,sha256=X7AXkrxMLYa0fUCdwZA2oOfiFkQJiuenTXzRghkc4eU,513
4
4
  fal/_serialization.py,sha256=rD2YiSa8iuzCaZohZwN_MPEB-PpSKbWRDeaIDpTEjyY,7653
5
5
  fal/_version.py,sha256=EBGqrknaf1WygENX-H4fBefLvHryvJBBGtVJetaB0NY,266
6
6
  fal/api.py,sha256=aoA0-7JsO6dWhudzmDOidbPwxnJmIJaQWhGV1kqLCbw,44814
@@ -12,18 +12,18 @@ fal/files.py,sha256=QgfYfMKmNobMPufrAP_ga1FKcIAlSbw18Iar1-0qepo,2650
12
12
  fal/flags.py,sha256=oWN_eidSUOcE9wdPK_77si3A1fpgOC0UEERPsvNLIMc,842
13
13
  fal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  fal/rest_client.py,sha256=kGBGmuyHfX1lR910EoKCYPjsyU8MdXawT_cW2q8Sajc,568
15
- fal/sdk.py,sha256=b-1J3nO5I4f69L2APqDZ0hLNo7qVhWA2bhCvrjhpQV4,25420
15
+ fal/sdk.py,sha256=lS-nVp35qwlovQWhmkhWhBmTMCG-ntMekdrIIgzK-2A,25423
16
16
  fal/sync.py,sha256=ZuIJA2-hTPNANG9B_NNJZUsO68EIdTH0dc9MzeVE2VU,4340
17
17
  fal/utils.py,sha256=9q_QrQBlQN3nZYA1kEGRfhJWi4RjnO4H1uQswfaei9w,2146
18
18
  fal/workflows.py,sha256=Zl4f6Bs085hY40zmqScxDUyCu7zXkukDbW02iYOLTTI,14805
19
- fal/auth/__init__.py,sha256=zk0CqYxwTFud-9vdh3og6zMWkFf_1gM5CbLrAP49lcQ,5837
19
+ fal/auth/__init__.py,sha256=WSDXxkrshGyOvfN6WomHGKrflbmJvPiDUSQRsp4mqsI,5932
20
20
  fal/auth/auth0.py,sha256=_OhfrqF41odNebFTr8SvUm-d9REVG6wfQBHhPIQxsZU,5468
21
21
  fal/auth/local.py,sha256=sndkM6vKpeVny6NHTacVlTbiIFqaksOmw0Viqs_RN1U,1790
22
22
  fal/cli/__init__.py,sha256=padK4o0BFqq61kxAA1qQ0jYr2SuhA2mf90B3AaRkmJA,37
23
23
  fal/cli/_utils.py,sha256=pHmKzpUWc2n4yPv4R0Y6DuZC5j-rVKU8oqdQDVW_-Lo,1591
24
24
  fal/cli/api.py,sha256=-rl50A00CxqVZtDh0iZmpCHMFY0jZySaumbPCe3MSoQ,2090
25
25
  fal/cli/apps.py,sha256=vKeTUw_uUxz5M9hlP0jcJ23qjR0GTz7ifeS4HBjKECo,10101
26
- fal/cli/auth.py,sha256=3wq2NYhlA3tFOSQOhQQnbzcPBMVWTU2IZzTEVWke1BY,2604
26
+ fal/cli/auth.py,sha256=iHXxreitWubR7meV7DEbSP8djvdb6QxMw6nHZFv9udw,3779
27
27
  fal/cli/cli_nested_json.py,sha256=veSZU8_bYV3Iu1PAoxt-4BMBraNIqgH5nughbs2UKvE,13539
28
28
  fal/cli/create.py,sha256=a8WDq-nJLFTeoIXqpb5cr7GR7YR9ZZrQCawNm34KXXE,627
29
29
  fal/cli/debug.py,sha256=u_urnyFzSlNnrq93zz_GXE9FX4VyVxDoamJJyrZpFI0,1312
@@ -36,7 +36,7 @@ fal/cli/profile.py,sha256=vWngqkX7UizQIUQOpXauFz1UGJwDeh38Si6wXcIj3Eo,3396
36
36
  fal/cli/run.py,sha256=nAC12Qss4Fg1XmV0qOS9RdGNLYcdoHeRgQMvbTN4P9I,1202
37
37
  fal/cli/runners.py,sha256=z7WkZZC9rCW2mU5enowVQsxd1W18iBtLNOnPjrzhEf0,3491
38
38
  fal/cli/secrets.py,sha256=QKSmazu-wiNF6fOpGL9v2TDYxAjX9KTi7ot7vnv6f5E,2474
39
- fal/cli/teams.py,sha256=ZrWdMvLRqPt1EuxoOKAfbP6sxJeOCaMeZ2LgZqw2S_w,2153
39
+ fal/cli/teams.py,sha256=lIY4uT8TGjk9g0z2tY4cGDU8PGqowncEMKh9K_dJYUY,1314
40
40
  fal/console/__init__.py,sha256=ernZ4bzvvliQh5SmrEqQ7lA5eVcbw6Ra2jalKtA7dxg,132
41
41
  fal/console/icons.py,sha256=De9MfFaSkO2Lqfne13n3PrYfTXJVIzYZVqYn5BWsdrA,108
42
42
  fal/console/ux.py,sha256=KMQs3UHQvVHDxDQQqlot-WskVKoMQXOE3jiVkkfmIMY,356
@@ -134,8 +134,8 @@ openapi_fal_rest/models/workflow_node_type.py,sha256=-FzyeY2bxcNmizKbJI8joG7byRi
134
134
  openapi_fal_rest/models/workflow_schema.py,sha256=4K5gsv9u9pxx2ItkffoyHeNjBBYf6ur5bN4m_zePZNY,2019
135
135
  openapi_fal_rest/models/workflow_schema_input.py,sha256=2OkOXWHTNsCXHWS6EGDFzcJKkW5FIap-2gfO233EvZQ,1191
136
136
  openapi_fal_rest/models/workflow_schema_output.py,sha256=EblwSPAGfWfYVWw_WSSaBzQVju296is9o28rMBAd0mc,1196
137
- fal-1.11.5.dist-info/METADATA,sha256=9ObQ73Rb1oolmwZPfD6veK3oeb0AGJ6EVq-QrxTXU28,4062
138
- fal-1.11.5.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
139
- fal-1.11.5.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
140
- fal-1.11.5.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
141
- fal-1.11.5.dist-info/RECORD,,
137
+ fal-1.12.0.dist-info/METADATA,sha256=aOSYnbFBCtE2_UnXsu0QrBd5s8sHWJUoOh6FwxqhSTM,4062
138
+ fal-1.12.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
139
+ fal-1.12.0.dist-info/entry_points.txt,sha256=32zwTUC1U1E7nSTIGCoANQOQ3I7-qHG5wI6gsVz5pNU,37
140
+ fal-1.12.0.dist-info/top_level.txt,sha256=r257X1L57oJL8_lM0tRrfGuXFwm66i1huwQygbpLmHw,21
141
+ fal-1.12.0.dist-info/RECORD,,
File without changes