qbraid-cli 0.8.0.dev0__py3-none-any.whl → 0.9.5__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 qbraid-cli might be problematic. Click here for more details.
- qbraid_cli/_version.py +2 -2
- qbraid_cli/account/__init__.py +11 -0
- qbraid_cli/account/app.py +65 -0
- qbraid_cli/admin/__init__.py +11 -0
- qbraid_cli/admin/app.py +59 -0
- qbraid_cli/admin/headers.py +235 -0
- qbraid_cli/admin/validation.py +32 -0
- qbraid_cli/chat/__init__.py +11 -0
- qbraid_cli/chat/app.py +76 -0
- qbraid_cli/configure/__init__.py +6 -1
- qbraid_cli/configure/actions.py +111 -0
- qbraid_cli/configure/app.py +34 -109
- qbraid_cli/devices/__init__.py +6 -1
- qbraid_cli/devices/app.py +43 -35
- qbraid_cli/devices/validation.py +26 -0
- qbraid_cli/envs/__init__.py +6 -1
- qbraid_cli/envs/activate.py +8 -5
- qbraid_cli/envs/app.py +215 -131
- qbraid_cli/envs/create.py +14 -120
- qbraid_cli/envs/data_handling.py +46 -0
- qbraid_cli/exceptions.py +3 -0
- qbraid_cli/files/__init__.py +11 -0
- qbraid_cli/files/app.py +118 -0
- qbraid_cli/handlers.py +47 -13
- qbraid_cli/jobs/__init__.py +6 -1
- qbraid_cli/jobs/app.py +84 -97
- qbraid_cli/jobs/toggle_braket.py +68 -74
- qbraid_cli/jobs/validation.py +94 -0
- qbraid_cli/kernels/__init__.py +6 -1
- qbraid_cli/kernels/app.py +81 -11
- qbraid_cli/main.py +66 -14
- qbraid_cli/pip/__init__.py +11 -0
- qbraid_cli/pip/app.py +50 -0
- qbraid_cli/pip/hooks.py +74 -0
- qbraid_cli/py.typed +0 -0
- qbraid_cli-0.9.5.dist-info/LICENSE +41 -0
- qbraid_cli-0.9.5.dist-info/METADATA +179 -0
- qbraid_cli-0.9.5.dist-info/RECORD +42 -0
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.9.5.dist-info}/WHEEL +1 -1
- qbraid_cli/credits/__init__.py +0 -6
- qbraid_cli/credits/app.py +0 -30
- qbraid_cli-0.8.0.dev0.dist-info/METADATA +0 -124
- qbraid_cli-0.8.0.dev0.dist-info/RECORD +0 -25
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.9.5.dist-info}/entry_points.txt +0 -0
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.9.5.dist-info}/top_level.txt +0 -0
qbraid_cli/files/app.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Module defining commands in the 'qbraid files' namespace.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import rich
|
|
13
|
+
import typer
|
|
14
|
+
|
|
15
|
+
from qbraid_cli.handlers import run_progress_task
|
|
16
|
+
|
|
17
|
+
files_app = typer.Typer(help="Manage qBraid cloud storage files.", no_args_is_help=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@files_app.command(name="upload")
|
|
21
|
+
def files_upload(
|
|
22
|
+
filepath: Path = typer.Argument(
|
|
23
|
+
...,
|
|
24
|
+
exists=True,
|
|
25
|
+
dir_okay=False,
|
|
26
|
+
resolve_path=True,
|
|
27
|
+
help="Local path to the file to upload.",
|
|
28
|
+
),
|
|
29
|
+
namespace: str = typer.Option(
|
|
30
|
+
"user",
|
|
31
|
+
"--namespace",
|
|
32
|
+
"-n",
|
|
33
|
+
help="Target qBraid namespace for the upload.",
|
|
34
|
+
),
|
|
35
|
+
object_path: str = typer.Option(
|
|
36
|
+
None,
|
|
37
|
+
"--object-path",
|
|
38
|
+
"-p",
|
|
39
|
+
help=("Target object path. " "Defaults to original filename in namespace root."),
|
|
40
|
+
),
|
|
41
|
+
overwrite: bool = typer.Option(
|
|
42
|
+
False,
|
|
43
|
+
"--overwrite",
|
|
44
|
+
"-o",
|
|
45
|
+
help="Overwrite existing file if it already exists in the target location.",
|
|
46
|
+
),
|
|
47
|
+
):
|
|
48
|
+
"""Upload a local file to qBraid storage."""
|
|
49
|
+
|
|
50
|
+
def upload_file() -> dict[str, Any]:
|
|
51
|
+
from qbraid_core.services.files import FileManagerClient
|
|
52
|
+
|
|
53
|
+
client = FileManagerClient()
|
|
54
|
+
data = client.upload_file(
|
|
55
|
+
filepath, namespace=namespace, object_path=object_path, overwrite=overwrite
|
|
56
|
+
)
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
data: dict = run_progress_task(
|
|
60
|
+
upload_file, description="Uploading file...", include_error_traceback=False
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
rich.print("File uploaded successfully!")
|
|
64
|
+
namespace = data.get("namespace")
|
|
65
|
+
object_path = data.get("objectPath")
|
|
66
|
+
|
|
67
|
+
if namespace and object_path:
|
|
68
|
+
rich.print(f"\nNamespace: '{namespace}'")
|
|
69
|
+
rich.print(f"Object path: '{object_path}'")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@files_app.command(name="download")
|
|
73
|
+
def files_download(
|
|
74
|
+
object_path: str = typer.Argument(
|
|
75
|
+
...,
|
|
76
|
+
help="The folder + filename describing the file to download.",
|
|
77
|
+
),
|
|
78
|
+
namespace: str = typer.Option(
|
|
79
|
+
"user",
|
|
80
|
+
"--namespace",
|
|
81
|
+
"-n",
|
|
82
|
+
help="Source qBraid namespace for the download.",
|
|
83
|
+
),
|
|
84
|
+
save_path: Path = typer.Option(
|
|
85
|
+
Path.cwd(),
|
|
86
|
+
"--save-path",
|
|
87
|
+
"-s",
|
|
88
|
+
resolve_path=True,
|
|
89
|
+
help="Local directory to save the downloaded file.",
|
|
90
|
+
),
|
|
91
|
+
overwrite: bool = typer.Option(
|
|
92
|
+
False,
|
|
93
|
+
"--overwrite",
|
|
94
|
+
"-o",
|
|
95
|
+
help="Overwrite existing file if it already exists in the target location.",
|
|
96
|
+
),
|
|
97
|
+
):
|
|
98
|
+
"""Download a file from qBraid storage."""
|
|
99
|
+
|
|
100
|
+
def download_file() -> Path:
|
|
101
|
+
from qbraid_core.services.files import FileManagerClient
|
|
102
|
+
|
|
103
|
+
client = FileManagerClient()
|
|
104
|
+
file_path = client.download_file(
|
|
105
|
+
object_path, namespace=namespace, save_path=save_path, overwrite=overwrite
|
|
106
|
+
)
|
|
107
|
+
return file_path
|
|
108
|
+
|
|
109
|
+
file_path: Path = run_progress_task(
|
|
110
|
+
download_file, description="Downloading file...", include_error_traceback=False
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
rich.print("File downloaded successfully!")
|
|
114
|
+
rich.print(f"Saved to: '{str(file_path)}'")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
files_app()
|
qbraid_cli/handlers.py
CHANGED
|
@@ -1,20 +1,38 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
1
4
|
"""
|
|
2
5
|
Module providing application support utilities, including abstractions for error handling
|
|
3
6
|
and executing operations with progress tracking within the qBraid CLI.
|
|
4
7
|
|
|
5
8
|
"""
|
|
6
9
|
|
|
10
|
+
import os
|
|
7
11
|
import traceback
|
|
8
12
|
from pathlib import Path
|
|
9
|
-
from
|
|
13
|
+
from time import sleep
|
|
14
|
+
from typing import Any, Callable, Optional, Union
|
|
10
15
|
|
|
11
16
|
import typer
|
|
12
17
|
from rich.console import Console
|
|
13
|
-
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
18
|
+
from rich.progress import Progress, SpinnerColumn, TaskID, TextColumn
|
|
14
19
|
|
|
15
20
|
from .exceptions import DEFAULT_ERROR_MESSAGE, QbraidException
|
|
16
21
|
|
|
17
22
|
|
|
23
|
+
def _should_display_progress():
|
|
24
|
+
"""Whether to display rich progress UI."""
|
|
25
|
+
return os.getenv("QBRAID_CLI_SHOW_PROGRESS", "true").lower() in ["true", "1", "t", "y", "yes"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _update_completed_task(
|
|
29
|
+
progress: Progress, task_id: TaskID, success: bool = True, sleep_time: float = 0.15
|
|
30
|
+
):
|
|
31
|
+
status = "Done" if success else "Failed"
|
|
32
|
+
progress.update(task_id, completed=100, status=status)
|
|
33
|
+
sleep(sleep_time)
|
|
34
|
+
|
|
35
|
+
|
|
18
36
|
def handle_error(
|
|
19
37
|
error_type: Optional[str] = None, message: Optional[str] = None, include_traceback: bool = True
|
|
20
38
|
) -> None:
|
|
@@ -38,10 +56,14 @@ def handle_error(
|
|
|
38
56
|
error_type = error_type or "Error"
|
|
39
57
|
message = message or DEFAULT_ERROR_MESSAGE
|
|
40
58
|
error_prefix = typer.style(f"{error_type}:", fg=typer.colors.RED, bold=True)
|
|
41
|
-
full_message = f"{error_prefix} {message}\n"
|
|
59
|
+
full_message = f"\n{error_prefix} {message}\n"
|
|
42
60
|
if include_traceback:
|
|
43
61
|
tb_string = traceback.format_exc()
|
|
44
|
-
|
|
62
|
+
# TODO: find out reason for weird traceback emitted from
|
|
63
|
+
# qbraid jobs enable/disable when library not installed.
|
|
64
|
+
# For now, if matches, just don't print it.
|
|
65
|
+
if tb_string.strip() != "NoneType: None":
|
|
66
|
+
full_message += f"\n{tb_string}"
|
|
45
67
|
typer.echo(full_message, err=True)
|
|
46
68
|
raise typer.Exit(code=1)
|
|
47
69
|
|
|
@@ -72,6 +94,7 @@ def run_progress_task(
|
|
|
72
94
|
*args,
|
|
73
95
|
description: Optional[str] = None,
|
|
74
96
|
error_message: Optional[str] = None,
|
|
97
|
+
include_error_traceback: bool = True,
|
|
75
98
|
**kwargs,
|
|
76
99
|
) -> Any:
|
|
77
100
|
"""
|
|
@@ -89,6 +112,8 @@ def run_progress_task(
|
|
|
89
112
|
error_message (optional, str): Custom error message to display if the operation.
|
|
90
113
|
fails. Defaults to None, in which case the
|
|
91
114
|
exception's message is used.
|
|
115
|
+
include_error_traceback (bool): Whether to include the traceback in the error message.
|
|
116
|
+
Defaults to True.
|
|
92
117
|
**kwargs: Arbitrary keyword arguments for the operation.
|
|
93
118
|
|
|
94
119
|
Returns:
|
|
@@ -97,6 +122,13 @@ def run_progress_task(
|
|
|
97
122
|
Raises:
|
|
98
123
|
typer.Exit: If the operation fails, after displaying the error message using typer.secho.
|
|
99
124
|
"""
|
|
125
|
+
if not _should_display_progress():
|
|
126
|
+
try:
|
|
127
|
+
return operation(*args, **kwargs)
|
|
128
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
129
|
+
custom_message = error_message if error_message else str(err)
|
|
130
|
+
return handle_error(message=custom_message, include_traceback=include_error_traceback)
|
|
131
|
+
|
|
100
132
|
console = Console()
|
|
101
133
|
with Progress(
|
|
102
134
|
"[progress.description]{task.description}",
|
|
@@ -108,21 +140,23 @@ def run_progress_task(
|
|
|
108
140
|
task = progress.add_task(description, status="In Progress", total=None)
|
|
109
141
|
try:
|
|
110
142
|
result = operation(*args, **kwargs)
|
|
111
|
-
progress
|
|
143
|
+
_update_completed_task(progress, task, success=True)
|
|
112
144
|
return result
|
|
113
|
-
except Exception as
|
|
114
|
-
progress
|
|
115
|
-
custom_message = error_message if error_message else str(
|
|
116
|
-
return handle_error(message=custom_message)
|
|
145
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
|
146
|
+
_update_completed_task(progress, task, success=False)
|
|
147
|
+
custom_message = error_message if error_message else str(err)
|
|
148
|
+
return handle_error(message=custom_message, include_traceback=include_error_traceback)
|
|
149
|
+
finally:
|
|
150
|
+
progress.remove_task(task)
|
|
117
151
|
|
|
118
152
|
|
|
119
|
-
def _format_list_items(items:
|
|
153
|
+
def _format_list_items(items: list[str]) -> str:
|
|
120
154
|
"""
|
|
121
155
|
Formats a list of items as a string with values comma-separated and
|
|
122
156
|
each item surrounded by single quotes
|
|
123
157
|
|
|
124
158
|
Args:
|
|
125
|
-
items (
|
|
159
|
+
items (list[str]): The list of items to format.
|
|
126
160
|
|
|
127
161
|
Returns:
|
|
128
162
|
str: The formatted string.
|
|
@@ -131,14 +165,14 @@ def _format_list_items(items: List[str]) -> str:
|
|
|
131
165
|
|
|
132
166
|
|
|
133
167
|
def validate_item(
|
|
134
|
-
value: Optional[str], allowed_items:
|
|
168
|
+
value: Optional[str], allowed_items: list[str], item_type: str
|
|
135
169
|
) -> Union[str, None]:
|
|
136
170
|
"""
|
|
137
171
|
Generic item validation function.
|
|
138
172
|
|
|
139
173
|
Args:
|
|
140
174
|
value (optional, str): The value to validate.
|
|
141
|
-
allowed_items (
|
|
175
|
+
allowed_items (list[str]): A list of allowed items.
|
|
142
176
|
item_type (str): A description of the item type (e.g., 'provider', 'status', 'type') for
|
|
143
177
|
error messages.
|
|
144
178
|
|
qbraid_cli/jobs/__init__.py
CHANGED
qbraid_cli/jobs/app.py
CHANGED
|
@@ -1,165 +1,152 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
1
4
|
"""
|
|
2
5
|
Module defining commands in the 'qbraid jobs' namespace.
|
|
3
6
|
|
|
4
7
|
"""
|
|
5
8
|
|
|
6
|
-
from typing import
|
|
9
|
+
from typing import Any, Callable
|
|
7
10
|
|
|
8
11
|
import typer
|
|
9
12
|
from rich.console import Console
|
|
10
13
|
|
|
11
|
-
from qbraid_cli.handlers import handle_error, run_progress_task
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
QJOB_LIBS = ["braket"]
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def validate_library(value: str) -> str:
|
|
19
|
-
"""Validate quantum jobs library."""
|
|
20
|
-
return validate_item(value, QJOB_LIBS, "Library")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
|
|
24
|
-
"""Get the state of qBraid Quantum Jobs for the specified library."""
|
|
25
|
-
from qbraid.api.system import qbraid_jobs_state
|
|
26
|
-
|
|
27
|
-
state_values = {}
|
|
14
|
+
from qbraid_cli.handlers import handle_error, run_progress_task
|
|
15
|
+
from qbraid_cli.jobs.toggle_braket import disable_braket, enable_braket
|
|
16
|
+
from qbraid_cli.jobs.validation import handle_jobs_state, run_progress_get_state, validate_library
|
|
28
17
|
|
|
29
|
-
|
|
30
|
-
libraries_to_check = [library]
|
|
31
|
-
else:
|
|
32
|
-
libraries_to_check = QJOB_LIBS
|
|
18
|
+
jobs_app = typer.Typer(help="Manage qBraid quantum jobs.", no_args_is_help=True)
|
|
33
19
|
|
|
34
|
-
for lib in libraries_to_check:
|
|
35
|
-
state_values[lib] = qbraid_jobs_state(lib)
|
|
36
20
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def run_progress_get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
|
|
41
|
-
"""Run get state function with rich progress UI."""
|
|
42
|
-
return run_progress_task(
|
|
43
|
-
get_state,
|
|
44
|
-
library,
|
|
45
|
-
description="Collecting package metadata...",
|
|
46
|
-
error_message=f"Failed to collect {library} package metadata.",
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def handle_jobs_state(
|
|
51
|
-
library: str,
|
|
52
|
-
action: str, # 'enable' or 'disable'
|
|
53
|
-
action_callback: Callable[[], None],
|
|
54
|
-
) -> None:
|
|
55
|
-
"""Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
|
|
56
|
-
state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
|
|
57
|
-
installed, enabled = state_values[library]
|
|
58
|
-
|
|
59
|
-
if not installed:
|
|
60
|
-
handle_error(message=f"{library} not installed.")
|
|
61
|
-
if (enabled and action == "enable") or (not enabled and action == "disable"):
|
|
62
|
-
action_color = "green" if enabled else "red"
|
|
63
|
-
console = Console()
|
|
64
|
-
console.print(
|
|
65
|
-
f"\nqBraid quantum jobs already [bold {action_color}]{action}d[/bold {action_color}] "
|
|
66
|
-
f"for [magenta]{library}[/magenta]."
|
|
67
|
-
)
|
|
68
|
-
console.print(
|
|
69
|
-
"To check the state of all quantum jobs libraries in this environment, "
|
|
70
|
-
"use: `[bold]qbraid jobs state[/bold]`"
|
|
71
|
-
)
|
|
72
|
-
raise typer.Exit()
|
|
73
|
-
|
|
74
|
-
action_callback() # Perform the specific enable/disable action
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@app.command(name="enable")
|
|
21
|
+
@jobs_app.command(name="enable")
|
|
78
22
|
def jobs_enable(
|
|
79
23
|
library: str = typer.Argument(
|
|
80
24
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
81
|
-
)
|
|
25
|
+
),
|
|
26
|
+
auto_confirm: bool = typer.Option(
|
|
27
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
28
|
+
),
|
|
82
29
|
) -> None:
|
|
83
30
|
"""Enable qBraid Quantum Jobs."""
|
|
84
31
|
|
|
85
32
|
def enable_action():
|
|
86
33
|
if library == "braket":
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
enable_braket()
|
|
34
|
+
enable_braket(auto_confirm=auto_confirm)
|
|
90
35
|
else:
|
|
91
36
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
92
37
|
|
|
93
38
|
handle_jobs_state(library, "enable", enable_action)
|
|
94
39
|
|
|
95
40
|
|
|
96
|
-
@
|
|
41
|
+
@jobs_app.command(name="disable")
|
|
97
42
|
def jobs_disable(
|
|
98
43
|
library: str = typer.Argument(
|
|
99
44
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
100
|
-
)
|
|
45
|
+
),
|
|
46
|
+
auto_confirm: bool = typer.Option(
|
|
47
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
48
|
+
),
|
|
101
49
|
) -> None:
|
|
102
50
|
"""Disable qBraid Quantum Jobs."""
|
|
103
51
|
|
|
104
52
|
def disable_action():
|
|
105
53
|
if library == "braket":
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
disable_braket()
|
|
54
|
+
disable_braket(auto_confirm=auto_confirm)
|
|
109
55
|
else:
|
|
110
56
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
111
57
|
|
|
112
58
|
handle_jobs_state(library, "disable", disable_action)
|
|
113
59
|
|
|
114
60
|
|
|
115
|
-
@
|
|
61
|
+
@jobs_app.command(name="state")
|
|
116
62
|
def jobs_state(
|
|
117
63
|
library: str = typer.Argument(
|
|
118
64
|
default=None,
|
|
119
65
|
help="Optional: Specify a software library with quantum jobs support to check its status.",
|
|
120
66
|
callback=validate_library,
|
|
121
|
-
)
|
|
67
|
+
),
|
|
122
68
|
) -> None:
|
|
123
69
|
"""Display the state of qBraid Quantum Jobs for the current environment."""
|
|
124
|
-
|
|
70
|
+
result: tuple[str, dict[str, tuple[bool, bool]]] = run_progress_get_state(library)
|
|
71
|
+
python_exe, state_values = result
|
|
72
|
+
state_values = dict(sorted(state_values.items()))
|
|
125
73
|
|
|
126
74
|
console = Console()
|
|
127
|
-
|
|
75
|
+
header_1, header_2 = "Library", "State"
|
|
76
|
+
max_lib_length = max((len(lib) for lib in state_values.keys()), default=len(header_1))
|
|
77
|
+
padding = max_lib_length + 9
|
|
128
78
|
|
|
79
|
+
output = ""
|
|
129
80
|
for lib, (installed, enabled) in state_values.items():
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
else
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.print(f"{lib:<12} n/a", end="")
|
|
81
|
+
state_str = (
|
|
82
|
+
"[green]enabled"
|
|
83
|
+
if enabled and installed
|
|
84
|
+
else "[red]disabled" if installed else "[grey70]unavailable"
|
|
85
|
+
)
|
|
86
|
+
output += f"{lib:<{padding-1}} {state_str}\n"
|
|
137
87
|
|
|
138
|
-
console.print("
|
|
88
|
+
console.print(f"Executable: {python_exe}")
|
|
89
|
+
console.print(f"\n{header_1:<{padding}}{header_2}", style="bold")
|
|
90
|
+
console.print(output)
|
|
139
91
|
|
|
140
92
|
|
|
141
|
-
@
|
|
93
|
+
@jobs_app.command(name="list")
|
|
142
94
|
def jobs_list(
|
|
143
95
|
limit: int = typer.Option(
|
|
144
96
|
10, "--limit", "-l", help="Limit the maximum number of results returned"
|
|
145
|
-
)
|
|
97
|
+
),
|
|
146
98
|
) -> None:
|
|
147
99
|
"""List qBraid Quantum Jobs."""
|
|
148
100
|
|
|
149
|
-
def import_jobs() ->
|
|
150
|
-
from
|
|
151
|
-
from qbraid.exceptions import QbraidError
|
|
101
|
+
def import_jobs() -> tuple[Any, Callable]:
|
|
102
|
+
from qbraid_core.services.quantum import QuantumClient, process_job_data
|
|
152
103
|
|
|
153
|
-
|
|
104
|
+
client = QuantumClient()
|
|
154
105
|
|
|
155
|
-
|
|
156
|
-
get_jobs, QbraidError = result
|
|
106
|
+
return client, process_job_data
|
|
157
107
|
|
|
108
|
+
result: tuple[Any, Callable] = run_progress_task(import_jobs)
|
|
109
|
+
client, process_job_data = result
|
|
110
|
+
# https://github.com/qBraid/api/issues/644
|
|
111
|
+
# raw_data = client.search_jobs(query={"numResults": limit})
|
|
112
|
+
raw_data = client.search_jobs(query={})
|
|
113
|
+
job_data, msg = process_job_data(raw_data)
|
|
114
|
+
job_data = job_data[:limit]
|
|
115
|
+
|
|
116
|
+
longest_job_id = max(len(item[0]) for item in job_data)
|
|
117
|
+
spacing = longest_job_id + 5
|
|
158
118
|
try:
|
|
159
|
-
|
|
160
|
-
|
|
119
|
+
console = Console()
|
|
120
|
+
header_1 = "Job ID"
|
|
121
|
+
header_2 = "Submitted"
|
|
122
|
+
header_3 = "Status"
|
|
123
|
+
console.print(f"[bold]{header_1.ljust(spacing)}{header_2.ljust(36)}{header_3}[/bold]")
|
|
124
|
+
for job_id, submitted, status in job_data:
|
|
125
|
+
if status == "COMPLETED":
|
|
126
|
+
status_color = "green"
|
|
127
|
+
elif status in ["FAILED", "CANCELLED"]:
|
|
128
|
+
status_color = "red"
|
|
129
|
+
elif status in [
|
|
130
|
+
"INITIALIZING",
|
|
131
|
+
"INITIALIZED",
|
|
132
|
+
"CREATED",
|
|
133
|
+
"QUEUED",
|
|
134
|
+
"VALIDATING",
|
|
135
|
+
"RUNNING",
|
|
136
|
+
]:
|
|
137
|
+
status_color = "blue"
|
|
138
|
+
else:
|
|
139
|
+
status_color = "grey"
|
|
140
|
+
console.print(
|
|
141
|
+
f"{job_id.ljust(spacing)}{submitted.ljust(35)}",
|
|
142
|
+
f"[{status_color}]{status}[/{status_color}]",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
console.print(f"\n{msg}", style="italic", justify="left")
|
|
146
|
+
|
|
147
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
161
148
|
handle_error(message="Failed to fetch quantum jobs.")
|
|
162
149
|
|
|
163
150
|
|
|
164
151
|
if __name__ == "__main__":
|
|
165
|
-
|
|
152
|
+
jobs_app()
|