Skip to content

Commit 200db6a

Browse files
github-actions[bot]patrick91
authored andcommitted
🎨 Auto format
1 parent 0ad8909 commit 200db6a

5 files changed

Lines changed: 75 additions & 127 deletions

File tree

‎src/fastapi_cloud_cli/commands/login.py‎

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,48 +77,50 @@ def login() -> Any:
7777
Login to FastAPI Cloud. 🚀
7878
"""
7979
identity = Identity()
80+
is_logged_in = identity.is_logged_in()
8081

81-
if identity.is_logged_in():
82-
with get_rich_toolkit(minimal=True) as toolkit:
82+
with get_rich_toolkit(minimal=is_logged_in) as toolkit:
83+
if is_logged_in:
8384
toolkit.print("You are already logged in.")
8485
toolkit.print(
8586
"Run [bold]fastapi cloud logout[/bold] first if you want to switch accounts."
8687
)
8788

88-
return
89+
return
8990

90-
if identity.has_deploy_token():
91-
with get_rich_toolkit() as toolkit:
91+
if identity.has_deploy_token():
9292
toolkit.print(
9393
"You have [bold blue]FASTAPI_CLOUD_TOKEN[/] environment variable set.\n"
9494
"This token will take precedence over the user token for "
9595
"[blue]`fastapi deploy`[/] command.",
9696
tag="Warning",
9797
)
9898

99-
with get_rich_toolkit() as toolkit, APIClient() as client:
100-
toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI")
99+
with APIClient() as client:
100+
toolkit.print_title("Login to FastAPI Cloud", tag="FastAPI")
101101

102-
toolkit.print_line()
102+
toolkit.print_line()
103103

104-
with toolkit.progress("Starting authorization") as progress:
105-
with client.handle_http_errors(progress):
106-
authorization_data = _start_device_authorization(client)
104+
with toolkit.progress("Starting authorization") as progress:
105+
with client.handle_http_errors(progress):
106+
authorization_data = _start_device_authorization(client)
107107

108-
url = authorization_data.verification_uri_complete
108+
url = authorization_data.verification_uri_complete
109109

110-
progress.log(f"Opening [link={url}]{url}[/link]")
110+
progress.log(f"Opening [link={url}]{url}[/link]")
111111

112-
toolkit.print_line()
112+
toolkit.print_line()
113113

114-
with toolkit.progress("Waiting for user to authorize...") as progress:
115-
typer.launch(url)
114+
with toolkit.progress("Waiting for user to authorize...") as progress:
115+
typer.launch(url)
116116

117-
with client.handle_http_errors(progress):
118-
access_token = _fetch_access_token(
119-
client, authorization_data.device_code, authorization_data.interval
120-
)
117+
with client.handle_http_errors(progress):
118+
access_token = _fetch_access_token(
119+
client,
120+
authorization_data.device_code,
121+
authorization_data.interval,
122+
)
121123

122-
write_auth_config(AuthConfig(access_token=access_token))
124+
write_auth_config(AuthConfig(access_token=access_token))
123125

124-
progress.log("Now you are logged in! 🚀")
126+
progress.log("Now you are logged in! 🚀")

‎src/fastapi_cloud_cli/commands/whoami.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ def whoami() -> Any:
1313

1414
with get_rich_toolkit(minimal=True) as toolkit:
1515
if not identity.is_logged_in():
16-
toolkit.print("No credentials found. Use [blue]`fastapi login`[/] to login.")
16+
toolkit.print(
17+
"No credentials found. Use [blue]`fastapi login`[/] to login."
18+
)
1719
else:
1820
with APIClient() as client:
1921
with toolkit.progress(

‎src/fastapi_cloud_cli/utils/cli.py‎

Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from types import TracebackType
44
from typing import Any, Literal
55

6-
import click
76
from rich.segment import Segment
87
from rich.style import Style
98
from rich.text import Text
@@ -16,7 +15,6 @@
1615
)
1716

1817
logger = logging.getLogger(__name__)
19-
VERSION_CHECK_CONTEXT_KEY = "fastapi_cloud_cli.version_check"
2018

2119

2220
class FastAPIStyle(TaggedStyle):
@@ -34,7 +32,9 @@ def _get_tag_segments(
3432
tag_segments, left_padding = super()._get_tag_segments(
3533
metadata, is_animated, done, animation_status=animation_status
3634
)
35+
3736
tag_style = metadata.get("tag_style")
37+
3838
if isinstance(tag_style, (str, Style)):
3939
style = self.console.get_style(tag_style)
4040
tag_segments = [
@@ -71,83 +71,42 @@ def __init__(
7171
self,
7272
style: BaseStyle | None = None,
7373
theme: RichToolkitTheme | None = None,
74-
handle_keyboard_interrupts: bool = True,
75-
print_spacing: bool = True,
7674
) -> None:
77-
super().__init__(
78-
style=style,
79-
theme=theme,
80-
handle_keyboard_interrupts=handle_keyboard_interrupts,
81-
)
82-
self._print_spacing = print_spacing
83-
self._version_check: BackgroundVersionCheck | None = None
84-
self._print_update_on_exit = False
85-
86-
def __enter__(self) -> "FastAPIRichToolkit":
75+
super().__init__(style=style, theme=theme)
8776
self._version_check = self._get_version_check()
8877

89-
if self._print_spacing:
90-
self.console.print()
91-
return self
92-
9378
def __exit__(
9479
self,
9580
exc_type: type[BaseException] | None,
9681
exc_value: BaseException | None,
9782
traceback: TracebackType | None,
9883
) -> bool | None:
99-
is_keyboard_interrupt = exc_type is KeyboardInterrupt
100-
101-
if is_keyboard_interrupt and self._version_check is not None:
102-
self._version_check.suppress()
103-
elif self._print_update_on_exit:
104-
self._print_update_message()
84+
self._print_update_message()
10585

106-
if self._print_spacing and not is_keyboard_interrupt:
107-
self.console.print()
108-
109-
if self.handle_keyboard_interrupts and is_keyboard_interrupt:
110-
return True
111-
112-
return None
86+
return super().__exit__(
87+
exc_type,
88+
exc_value,
89+
traceback,
90+
)
11391

11492
def _get_version_check(self) -> BackgroundVersionCheck | None:
11593
if os.environ.get(DISABLE_VERSION_CHECK_ENV) == "1":
11694
return None
11795

118-
context = click.get_current_context(silent=True)
119-
if context is None:
120-
version_check = BackgroundVersionCheck()
121-
version_check.start()
122-
self._print_update_on_exit = True
123-
return version_check
124-
125-
stored_version_check = context.meta.get(VERSION_CHECK_CONTEXT_KEY)
126-
if isinstance(stored_version_check, BackgroundVersionCheck):
127-
return stored_version_check
128-
12996
version_check = BackgroundVersionCheck()
13097
version_check.start()
131-
context.meta[VERSION_CHECK_CONTEXT_KEY] = version_check
132-
context.call_on_close(self._print_update_message)
13398

13499
return version_check
135100

136101
def _print_update_message(self) -> None:
137102
if self._version_check is None:
138103
return
139104

140-
message = self._version_check.get_update_message()
141-
if message:
105+
if message := self._version_check.get_update_message():
142106
self.print(Text.from_markup(message), tag="update", tag_style="tag.update")
143107

144108

145-
def get_rich_toolkit(
146-
minimal: bool = False,
147-
*,
148-
print_spacing: bool = True,
149-
handle_keyboard_interrupts: bool = True,
150-
) -> RichToolkit:
109+
def get_rich_toolkit(minimal: bool = False) -> RichToolkit:
151110
style = MinimalStyle() if minimal else FastAPIStyle(tag_width=11)
152111

153112
theme = RichToolkitTheme(
@@ -166,8 +125,4 @@ def get_rich_toolkit(
166125
},
167126
)
168127

169-
return FastAPIRichToolkit(
170-
theme=theme,
171-
handle_keyboard_interrupts=handle_keyboard_interrupts,
172-
print_spacing=print_spacing,
173-
)
128+
return FastAPIRichToolkit(theme=theme)

‎src/fastapi_cloud_cli/utils/version_check.py‎

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import re
44
import threading
55
from collections.abc import Callable
6+
from contextlib import suppress
67
from dataclasses import dataclass
78
from datetime import datetime, timedelta, timezone
89
from pathlib import Path
@@ -176,11 +177,7 @@ def get_upgrade_command(
176177
*,
177178
detector: Callable[[str], InstallerInfo | None] = detect_installer,
178179
) -> str:
179-
try:
180-
installer_info = detector(PACKAGE_NAME)
181-
except Exception as error:
182-
logger.debug("Could not detect CLI installer: %s", error)
183-
return DEFAULT_UPGRADE_COMMAND
180+
installer_info = detector(PACKAGE_NAME)
184181

185182
if installer_info is None or installer_info.upgrade_cmd is None:
186183
return DEFAULT_UPGRADE_COMMAND
@@ -211,29 +208,22 @@ def __init__(
211208
) -> None:
212209
self._check_for_update = check_for_update
213210
self._join_timeout = join_timeout
214-
self._thread: threading.Thread | None = None
211+
self._thread = threading.Thread(target=self._run, daemon=True)
215212
self._update: VersionUpdate | None = None
216213
self._message_returned = False
217214

218215
def start(self) -> None:
219-
self._thread = threading.Thread(target=self._run, daemon=True)
220216
self._thread.start()
221217

222218
def _run(self) -> None:
223-
try:
219+
with suppress(Exception):
224220
self._update = self._check_for_update()
225-
except Exception as error:
226-
logger.debug("Could not check latest CLI version: %s", error)
227-
228-
def suppress(self) -> None:
229-
self._message_returned = True
230221

231222
def get_update_message(self) -> str | None:
232223
if self._message_returned:
233224
return None
234225

235-
if self._thread:
236-
self._thread.join(timeout=self._join_timeout)
226+
self._thread.join(timeout=self._join_timeout)
237227

238228
if self._update:
239229
self._message_returned = True

‎tests/test_version_check.py‎

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import inspect
21
import json
32
import os
43
from datetime import datetime, timedelta, timezone
@@ -37,10 +36,6 @@ def test_is_newer_version() -> None:
3736
assert not is_newer_version("not-a-version", "0.17.1")
3837

3938

40-
def test_get_rich_toolkit_has_no_invocation_args_override() -> None:
41-
assert "args" not in inspect.signature(get_rich_toolkit).parameters
42-
43-
4439
@respx.mock
4540
def test_check_for_update_returns_update_when_pypi_has_newer_version() -> None:
4641
route = respx.get(PYPI_JSON_URL).mock(
@@ -225,6 +220,14 @@ def test_get_upgrade_command_falls_back_when_installer_is_unknown() -> None:
225220
)
226221

227222

223+
def test_get_upgrade_command_propagates_detector_errors() -> None:
224+
def broken_detector(package_name: str) -> None:
225+
raise RuntimeError(f"Could not inspect {package_name}")
226+
227+
with pytest.raises(RuntimeError, match="Could not inspect fastapi-cloud-cli"):
228+
get_upgrade_command(detector=broken_detector)
229+
230+
228231
def test_format_update_message() -> None:
229232
message = format_update_message(
230233
VersionUpdate(current="0.17.1", latest="0.18.0"),
@@ -245,44 +248,23 @@ def _seed_fresh_update_cache(latest_version: str = "999.0.0") -> None:
245248
)
246249

247250

248-
def test_get_rich_toolkit_prints_forced_update_message_without_wrapper(
249-
monkeypatch: pytest.MonkeyPatch,
250-
capsys: pytest.CaptureFixture[str],
251-
) -> None:
252-
monkeypatch.setenv("FASTAPI_CLOUD_DISABLE_VERSION_CHECK", "")
253-
_seed_fresh_update_cache()
254-
255-
with get_rich_toolkit() as toolkit:
256-
toolkit.print("command output")
257-
258-
output = capsys.readouterr().out
259-
assert "command output" in output
260-
assert "update" in output
261-
assert "A newer FastAPI Cloud CLI version is available" in output
262-
263-
264-
def test_get_rich_toolkit_prints_forced_update_once_per_click_command() -> None:
251+
def test_get_rich_toolkit_prints_update_after_toolkit_output() -> None:
265252
@click.command()
266-
def command_with_multiple_toolkits() -> None:
267-
with get_rich_toolkit() as toolkit:
268-
toolkit.print("first output")
253+
def command_with_toolkit() -> None:
269254
with get_rich_toolkit() as toolkit:
270-
toolkit.print("second output")
271-
click.echo("after command body")
255+
toolkit.print("command output")
272256

273257
_seed_fresh_update_cache()
274258
result = CliRunner().invoke(
275-
command_with_multiple_toolkits,
259+
command_with_toolkit,
276260
[],
277261
env={"FASTAPI_CLOUD_DISABLE_VERSION_CHECK": ""},
278262
)
279263

280264
assert result.exit_code == 0, result.output
281-
assert "first output" in result.output
282-
assert "second output" in result.output
283-
assert "after command body" in result.output
265+
assert "command output" in result.output
284266
assert result.output.count("A newer FastAPI Cloud CLI version is available") == 1
285-
assert result.output.rfind("after command body") < result.output.rfind("A newer")
267+
assert result.output.rfind("command output") < result.output.rfind("A newer")
286268

287269

288270
def test_get_rich_toolkit_skips_update_when_disabled(
@@ -322,3 +304,20 @@ def test_background_check_returns_no_message_without_update() -> None:
322304
message = check.get_update_message()
323305

324306
assert message is None
307+
308+
309+
def test_background_check_hides_unexpected_errors(
310+
recwarn: pytest.WarningsRecorder,
311+
) -> None:
312+
def broken_check() -> None:
313+
raise RuntimeError("version check failed unexpectedly")
314+
315+
check = BackgroundVersionCheck(check_for_update=broken_check, join_timeout=1)
316+
317+
check.start()
318+
319+
assert check.get_update_message() is None
320+
assert not any(
321+
warning.category is pytest.PytestUnhandledThreadExceptionWarning
322+
for warning in recwarn
323+
)

0 commit comments

Comments
 (0)