qbraid-cli 0.8.0.dev1__py3-none-any.whl → 0.8.0.dev4__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/configure/__init__.py +4 -1
- qbraid_cli/configure/actions.py +111 -0
- qbraid_cli/configure/app.py +11 -108
- qbraid_cli/credits/__init__.py +4 -1
- qbraid_cli/credits/app.py +12 -10
- qbraid_cli/devices/__init__.py +4 -1
- qbraid_cli/devices/app.py +43 -35
- qbraid_cli/devices/validation.py +27 -0
- qbraid_cli/envs/__init__.py +4 -1
- qbraid_cli/envs/activate.py +8 -5
- qbraid_cli/envs/app.py +38 -155
- qbraid_cli/envs/create.py +3 -0
- qbraid_cli/envs/data_handling.py +140 -0
- qbraid_cli/exceptions.py +3 -0
- qbraid_cli/handlers.py +8 -1
- qbraid_cli/jobs/__init__.py +4 -1
- qbraid_cli/jobs/app.py +69 -89
- qbraid_cli/jobs/toggle_braket.py +38 -39
- qbraid_cli/jobs/validation.py +93 -0
- qbraid_cli/kernels/__init__.py +4 -1
- qbraid_cli/kernels/app.py +81 -12
- qbraid_cli/main.py +15 -8
- {qbraid_cli-0.8.0.dev1.dist-info → qbraid_cli-0.8.0.dev4.dist-info}/METADATA +15 -9
- qbraid_cli-0.8.0.dev4.dist-info/RECORD +29 -0
- {qbraid_cli-0.8.0.dev1.dist-info → qbraid_cli-0.8.0.dev4.dist-info}/WHEEL +1 -1
- qbraid_cli-0.8.0.dev1.dist-info/RECORD +0 -25
- {qbraid_cli-0.8.0.dev1.dist-info → qbraid_cli-0.8.0.dev4.dist-info}/entry_points.txt +0 -0
- {qbraid_cli-0.8.0.dev1.dist-info → qbraid_cli-0.8.0.dev4.dist-info}/top_level.txt +0 -0
qbraid_cli/jobs/app.py
CHANGED
|
@@ -1,119 +1,63 @@
|
|
|
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
|
-
|
|
6
|
-
import sys
|
|
7
|
-
from typing import Callable, Dict, Optional, Tuple
|
|
8
|
+
from typing import Any, Callable, Dict, Tuple
|
|
8
9
|
|
|
9
10
|
import typer
|
|
10
11
|
from rich.console import Console
|
|
11
12
|
|
|
12
|
-
from qbraid_cli.handlers import handle_error, run_progress_task
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
QJOB_LIBS = ["braket"]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def validate_library(value: str) -> str:
|
|
20
|
-
"""Validate quantum jobs library."""
|
|
21
|
-
return validate_item(value, QJOB_LIBS, "Library")
|
|
22
|
-
|
|
13
|
+
from qbraid_cli.handlers import handle_error, run_progress_task
|
|
14
|
+
from qbraid_cli.jobs.toggle_braket import disable_braket, enable_braket
|
|
15
|
+
from qbraid_cli.jobs.validation import handle_jobs_state, run_progress_get_state, validate_library
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
"""Get the state of qBraid Quantum Jobs for the specified library."""
|
|
26
|
-
from qbraid.api.system import qbraid_jobs_state
|
|
27
|
-
|
|
28
|
-
state_values = {}
|
|
29
|
-
|
|
30
|
-
if library:
|
|
31
|
-
libraries_to_check = [library]
|
|
32
|
-
else:
|
|
33
|
-
libraries_to_check = QJOB_LIBS
|
|
34
|
-
|
|
35
|
-
for lib in libraries_to_check:
|
|
36
|
-
state_values[lib] = qbraid_jobs_state(lib)
|
|
37
|
-
|
|
38
|
-
return state_values
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def run_progress_get_state(library: Optional[str] = None) -> Dict[str, Tuple[bool, bool]]:
|
|
42
|
-
"""Run get state function with rich progress UI."""
|
|
43
|
-
return run_progress_task(
|
|
44
|
-
get_state,
|
|
45
|
-
library,
|
|
46
|
-
description="Collecting package metadata...",
|
|
47
|
-
error_message=f"Failed to collect {library} package metadata.",
|
|
48
|
-
)
|
|
17
|
+
jobs_app = typer.Typer(help="Manage qBraid quantum jobs.")
|
|
49
18
|
|
|
50
19
|
|
|
51
|
-
|
|
52
|
-
library: str,
|
|
53
|
-
action: str, # 'enable' or 'disable'
|
|
54
|
-
action_callback: Callable[[], None],
|
|
55
|
-
) -> None:
|
|
56
|
-
"""Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
|
|
57
|
-
state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
|
|
58
|
-
installed, enabled = state_values[library]
|
|
59
|
-
|
|
60
|
-
if not installed:
|
|
61
|
-
handle_error(message=f"{library} not installed.")
|
|
62
|
-
if (enabled and action == "enable") or (not enabled and action == "disable"):
|
|
63
|
-
action_color = "green" if enabled else "red"
|
|
64
|
-
console = Console()
|
|
65
|
-
console.print(
|
|
66
|
-
f"\nqBraid quantum jobs already [bold {action_color}]{action}d[/bold {action_color}] "
|
|
67
|
-
f"for [magenta]{library}[/magenta]."
|
|
68
|
-
)
|
|
69
|
-
console.print(
|
|
70
|
-
"To check the state of all quantum jobs libraries in this environment, "
|
|
71
|
-
"use: `[bold]qbraid jobs state[/bold]`"
|
|
72
|
-
)
|
|
73
|
-
raise typer.Exit()
|
|
74
|
-
|
|
75
|
-
action_callback() # Perform the specific enable/disable action
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@app.command(name="enable")
|
|
20
|
+
@jobs_app.command(name="enable")
|
|
79
21
|
def jobs_enable(
|
|
80
22
|
library: str = typer.Argument(
|
|
81
23
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
82
|
-
)
|
|
24
|
+
),
|
|
25
|
+
auto_confirm: bool = typer.Option(
|
|
26
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
27
|
+
),
|
|
83
28
|
) -> None:
|
|
84
29
|
"""Enable qBraid Quantum Jobs."""
|
|
85
30
|
|
|
86
31
|
def enable_action():
|
|
87
32
|
if library == "braket":
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
enable_braket()
|
|
33
|
+
enable_braket(auto_confirm=auto_confirm)
|
|
91
34
|
else:
|
|
92
35
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
93
36
|
|
|
94
37
|
handle_jobs_state(library, "enable", enable_action)
|
|
95
38
|
|
|
96
39
|
|
|
97
|
-
@
|
|
40
|
+
@jobs_app.command(name="disable")
|
|
98
41
|
def jobs_disable(
|
|
99
42
|
library: str = typer.Argument(
|
|
100
43
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
101
|
-
)
|
|
44
|
+
),
|
|
45
|
+
auto_confirm: bool = typer.Option(
|
|
46
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
47
|
+
),
|
|
102
48
|
) -> None:
|
|
103
49
|
"""Disable qBraid Quantum Jobs."""
|
|
104
50
|
|
|
105
51
|
def disable_action():
|
|
106
52
|
if library == "braket":
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
disable_braket()
|
|
53
|
+
disable_braket(auto_confirm=auto_confirm)
|
|
110
54
|
else:
|
|
111
55
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
112
56
|
|
|
113
57
|
handle_jobs_state(library, "disable", disable_action)
|
|
114
58
|
|
|
115
59
|
|
|
116
|
-
@
|
|
60
|
+
@jobs_app.command(name="state")
|
|
117
61
|
def jobs_state(
|
|
118
62
|
library: str = typer.Argument(
|
|
119
63
|
default=None,
|
|
@@ -122,7 +66,8 @@ def jobs_state(
|
|
|
122
66
|
)
|
|
123
67
|
) -> None:
|
|
124
68
|
"""Display the state of qBraid Quantum Jobs for the current environment."""
|
|
125
|
-
|
|
69
|
+
result: Tuple[str, Dict[str, Tuple[bool, bool]]] = run_progress_get_state(library)
|
|
70
|
+
python_exe, state_values = result
|
|
126
71
|
state_values = dict(sorted(state_values.items()))
|
|
127
72
|
|
|
128
73
|
console = Console()
|
|
@@ -130,7 +75,7 @@ def jobs_state(
|
|
|
130
75
|
max_lib_length = max((len(lib) for lib in state_values.keys()), default=len(header_1))
|
|
131
76
|
padding = max_lib_length + 9
|
|
132
77
|
|
|
133
|
-
console.print(f"Executable: {
|
|
78
|
+
console.print(f"Executable: {python_exe}")
|
|
134
79
|
console.print(f"\n{header_1:<{padding}}{header_2}", style="bold")
|
|
135
80
|
|
|
136
81
|
for lib, (installed, enabled) in state_values.items():
|
|
@@ -142,7 +87,7 @@ def jobs_state(
|
|
|
142
87
|
console.print(f"{lib:<{padding-1}}", state_str, end="\n")
|
|
143
88
|
|
|
144
89
|
|
|
145
|
-
@
|
|
90
|
+
@jobs_app.command(name="list")
|
|
146
91
|
def jobs_list(
|
|
147
92
|
limit: int = typer.Option(
|
|
148
93
|
10, "--limit", "-l", help="Limit the maximum number of results returned"
|
|
@@ -150,20 +95,55 @@ def jobs_list(
|
|
|
150
95
|
) -> None:
|
|
151
96
|
"""List qBraid Quantum Jobs."""
|
|
152
97
|
|
|
153
|
-
def import_jobs() -> Tuple[
|
|
154
|
-
from
|
|
155
|
-
|
|
98
|
+
def import_jobs() -> Tuple[Any, Callable]:
|
|
99
|
+
from qbraid_core.services.quantum import QuantumClient, process_job_data
|
|
100
|
+
|
|
101
|
+
client = QuantumClient()
|
|
156
102
|
|
|
157
|
-
return
|
|
103
|
+
return client, process_job_data
|
|
158
104
|
|
|
159
|
-
result: Tuple[
|
|
160
|
-
|
|
105
|
+
result: Tuple[Any, Callable] = run_progress_task(import_jobs)
|
|
106
|
+
client, process_job_data = result
|
|
107
|
+
# https://github.com/qBraid/api/issues/644
|
|
108
|
+
# raw_data = client.search_jobs(query={"numResults": limit})
|
|
109
|
+
raw_data = client.search_jobs(query={})
|
|
110
|
+
job_data, msg = process_job_data(raw_data)
|
|
111
|
+
job_data = job_data[:limit]
|
|
161
112
|
|
|
113
|
+
longest_job_id = max(len(item[0]) for item in job_data)
|
|
114
|
+
spacing = longest_job_id + 5
|
|
162
115
|
try:
|
|
163
|
-
|
|
164
|
-
|
|
116
|
+
console = Console()
|
|
117
|
+
header_1 = "Job ID"
|
|
118
|
+
header_2 = "Submitted"
|
|
119
|
+
header_3 = "Status"
|
|
120
|
+
console.print(f"\n[bold]{header_1.ljust(spacing)}{header_2.ljust(36)}{header_3}[/bold]")
|
|
121
|
+
for job_id, submitted, status in job_data:
|
|
122
|
+
if status == "COMPLETED":
|
|
123
|
+
status_color = "green"
|
|
124
|
+
elif status in ["FAILED", "CANCELLED"]:
|
|
125
|
+
status_color = "red"
|
|
126
|
+
elif status in [
|
|
127
|
+
"INITIALIZING",
|
|
128
|
+
"INITIALIZED",
|
|
129
|
+
"CREATED",
|
|
130
|
+
"QUEUED",
|
|
131
|
+
"VALIDATING",
|
|
132
|
+
"RUNNING",
|
|
133
|
+
]:
|
|
134
|
+
status_color = "blue"
|
|
135
|
+
else:
|
|
136
|
+
status_color = "grey"
|
|
137
|
+
console.print(
|
|
138
|
+
f"{job_id.ljust(spacing)}{submitted.ljust(35)}",
|
|
139
|
+
f"[{status_color}]{status}[/{status_color}]",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
console.print(f"\n{msg}", style="italic", justify="left")
|
|
143
|
+
|
|
144
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
165
145
|
handle_error(message="Failed to fetch quantum jobs.")
|
|
166
146
|
|
|
167
147
|
|
|
168
148
|
if __name__ == "__main__":
|
|
169
|
-
|
|
149
|
+
jobs_app()
|
qbraid_cli/jobs/toggle_braket.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
1
4
|
"""
|
|
2
5
|
Module supporting 'qbraid jobs enable/disable braket' and commands.
|
|
3
6
|
|
|
@@ -6,7 +9,6 @@ Module supporting 'qbraid jobs enable/disable braket' and commands.
|
|
|
6
9
|
import logging
|
|
7
10
|
import os
|
|
8
11
|
import subprocess
|
|
9
|
-
import sys
|
|
10
12
|
from pathlib import Path
|
|
11
13
|
from typing import Optional, Tuple
|
|
12
14
|
|
|
@@ -19,39 +21,38 @@ logging.basicConfig(level=logging.INFO)
|
|
|
19
21
|
logger = logging.getLogger(__name__)
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
def get_package_data(package: str) -> Tuple[str, str, str]:
|
|
24
|
+
def get_package_data(package: str) -> Tuple[str, str, str, str]:
|
|
23
25
|
"""Retrieve package version and location data.
|
|
24
26
|
|
|
25
27
|
Args:
|
|
26
28
|
package (str): The name of the package to retrieve data for.
|
|
27
29
|
|
|
28
30
|
Returns:
|
|
29
|
-
Tuple[str, str, str]: The installed and latest versions of the package, and the
|
|
30
|
-
|
|
31
|
+
Tuple[str, str, str, str]: The installed and latest versions of the package, and the
|
|
32
|
+
local site-packages path where it is / would be installed.
|
|
31
33
|
|
|
32
34
|
Raises:
|
|
33
35
|
QbraidException: If package version or location data cannot be retrieved.
|
|
34
36
|
|
|
35
37
|
"""
|
|
36
|
-
|
|
38
|
+
# pylint: disable=import-outside-toplevel
|
|
39
|
+
from qbraid_core.system import (
|
|
40
|
+
QbraidSystemError,
|
|
41
|
+
get_active_python_path,
|
|
37
42
|
get_active_site_packages_path,
|
|
38
43
|
get_latest_package_version,
|
|
39
44
|
get_local_package_version,
|
|
40
45
|
)
|
|
41
|
-
from qbraid.exceptions import QbraidError
|
|
42
46
|
|
|
43
47
|
try:
|
|
44
|
-
|
|
48
|
+
python_pathlib = get_active_python_path()
|
|
49
|
+
site_packages_path = get_active_site_packages_path(python_path=python_pathlib)
|
|
50
|
+
installed_version = get_local_package_version(package, python_path=python_pathlib)
|
|
45
51
|
latest_version = get_latest_package_version(package)
|
|
46
|
-
except
|
|
47
|
-
raise QbraidException("Failed to retrieve package
|
|
48
|
-
|
|
49
|
-
try:
|
|
50
|
-
site_packages_path = get_active_site_packages_path()
|
|
51
|
-
except QbraidError as err:
|
|
52
|
-
raise QbraidException("Failed to retrieve site-package location") from err
|
|
52
|
+
except QbraidSystemError as err:
|
|
53
|
+
raise QbraidException("Failed to retrieve required system and/or package metadata") from err
|
|
53
54
|
|
|
54
|
-
return installed_version, latest_version, site_packages_path
|
|
55
|
+
return installed_version, latest_version, str(site_packages_path), str(python_pathlib)
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
def confirm_updates(
|
|
@@ -70,9 +71,6 @@ def confirm_updates(
|
|
|
70
71
|
installed_version (optional, str): The installed version of the target package.
|
|
71
72
|
latest_version (optional, str): The latest version of the target package available on PyPI.
|
|
72
73
|
|
|
73
|
-
Returns:
|
|
74
|
-
None
|
|
75
|
-
|
|
76
74
|
Raises:
|
|
77
75
|
ValueError: If an invalid mode is provided.
|
|
78
76
|
typer.Exit: If the user declines to proceed with enabling or disabling qBraid Quantum Jobs.
|
|
@@ -117,8 +115,6 @@ def confirm_updates(
|
|
|
117
115
|
typer.echo("\nqBraidSystemExit: Exiting.")
|
|
118
116
|
raise typer.Exit()
|
|
119
117
|
|
|
120
|
-
typer.echo("")
|
|
121
|
-
|
|
122
118
|
|
|
123
119
|
def aws_configure_dummy() -> None:
|
|
124
120
|
"""
|
|
@@ -154,44 +150,47 @@ def aws_configure_dummy() -> None:
|
|
|
154
150
|
handle_error(message="Failed to configure qBraid quantum jobs.")
|
|
155
151
|
|
|
156
152
|
|
|
157
|
-
def enable_braket():
|
|
153
|
+
def enable_braket(auto_confirm: bool = False):
|
|
158
154
|
"""Enable qBraid quantum jobs for Amazon Braket."""
|
|
159
|
-
|
|
155
|
+
installed, latest, path, python_exe = run_progress_task(
|
|
160
156
|
get_package_data, "boto3", description="Solving environment..."
|
|
161
157
|
)
|
|
162
|
-
|
|
158
|
+
|
|
159
|
+
if not auto_confirm:
|
|
160
|
+
confirm_updates("enable", path, installed_version=installed, latest_version=latest)
|
|
161
|
+
typer.echo("")
|
|
162
|
+
|
|
163
163
|
aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
|
|
164
164
|
|
|
165
165
|
try:
|
|
166
|
-
subprocess.check_call([
|
|
167
|
-
subprocess.check_call(
|
|
168
|
-
[sys.executable, "-m", "pip", "uninstall", "botocore", "-y", "--quiet"]
|
|
169
|
-
)
|
|
166
|
+
subprocess.check_call([python_exe, "-m", "pip", "install", "--upgrade", "boto3"])
|
|
167
|
+
subprocess.check_call([python_exe, "-m", "pip", "uninstall", "botocore", "-y", "--quiet"])
|
|
170
168
|
subprocess.check_call(
|
|
171
|
-
[
|
|
169
|
+
[python_exe, "-m", "pip", "install", "git+https://github.com/qBraid/botocore.git"]
|
|
172
170
|
)
|
|
173
|
-
except subprocess.CalledProcessError:
|
|
171
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
174
172
|
handle_error(message="Failed to enable qBraid quantum jobs.")
|
|
175
173
|
|
|
176
174
|
typer.secho("\nSuccessfully enabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
|
|
177
|
-
typer.secho(
|
|
178
|
-
"\nTo disable, run: `qbraid jobs disable braket`\n", fg=typer.colors.GREEN, bold=True
|
|
179
|
-
)
|
|
175
|
+
typer.secho("\nTo disable, run: \n\n\t$ qbraid jobs disable braket\n")
|
|
180
176
|
|
|
181
177
|
|
|
182
|
-
def disable_braket():
|
|
178
|
+
def disable_braket(auto_confirm: bool = False):
|
|
183
179
|
"""Disable qBraid quantum jobs for Amazon Braket."""
|
|
184
180
|
package = "botocore"
|
|
185
|
-
|
|
181
|
+
installed, latest, path, python_exe = run_progress_task(
|
|
186
182
|
get_package_data, package, description="Solving environment..."
|
|
187
183
|
)
|
|
188
|
-
package = f"{package}~={
|
|
189
|
-
|
|
184
|
+
package = f"{package}~={installed}" if installed < latest else package
|
|
185
|
+
|
|
186
|
+
if not auto_confirm:
|
|
187
|
+
confirm_updates("disable", path)
|
|
188
|
+
typer.echo("")
|
|
190
189
|
|
|
191
190
|
try:
|
|
192
191
|
subprocess.check_call(
|
|
193
192
|
[
|
|
194
|
-
|
|
193
|
+
python_exe,
|
|
195
194
|
"-m",
|
|
196
195
|
"pip",
|
|
197
196
|
"install",
|
|
@@ -200,8 +199,8 @@ def disable_braket():
|
|
|
200
199
|
],
|
|
201
200
|
stderr=subprocess.DEVNULL,
|
|
202
201
|
)
|
|
203
|
-
except subprocess.CalledProcessError:
|
|
202
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
204
203
|
handle_error(message="Failed to disable qBraid quantum jobs.")
|
|
205
204
|
|
|
206
205
|
typer.secho("\nSuccessfully disabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
|
|
207
|
-
typer.secho("\nTo enable, run:
|
|
206
|
+
typer.secho("\nTo enable, run: \n\n\t$ qbraid jobs enable braket\n")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Module for validating command arguments for qBraid Quantum Jobs.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
from qbraid_cli.handlers import handle_error, run_progress_task, validate_item
|
|
15
|
+
|
|
16
|
+
LEGACY_ARGS: Dict[str, str] = {
|
|
17
|
+
"amazon_braket": "braket",
|
|
18
|
+
"aws_braket": "braket",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def validate_library(value: str) -> str:
|
|
23
|
+
"""Validate quantum jobs library."""
|
|
24
|
+
# pylint:disable-next=import-outside-toplevel
|
|
25
|
+
from qbraid_core.services.quantum.proxy import SUPPORTED_QJOB_LIBS
|
|
26
|
+
|
|
27
|
+
qjobs_libs = list(SUPPORTED_QJOB_LIBS.keys())
|
|
28
|
+
|
|
29
|
+
if value in LEGACY_ARGS:
|
|
30
|
+
old_value = value
|
|
31
|
+
value = LEGACY_ARGS[value]
|
|
32
|
+
|
|
33
|
+
console = Console()
|
|
34
|
+
console.print(
|
|
35
|
+
f"[red]DeprecationWarning:[/red] Argument '{old_value}' "
|
|
36
|
+
f"is deprecated. Use '{value}' instead.\n"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return validate_item(value, qjobs_libs, "Library")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_state(library: Optional[str] = None) -> Tuple[str, Dict[str, Tuple[bool, bool]]]:
|
|
43
|
+
"""Get the state of qBraid Quantum Jobs for the specified library."""
|
|
44
|
+
from qbraid_core.services.quantum import QuantumClient
|
|
45
|
+
|
|
46
|
+
jobs_state = QuantumClient.qbraid_jobs_state(device_lib=library)
|
|
47
|
+
|
|
48
|
+
python_exe: str = jobs_state.get("exe", sys.executable)
|
|
49
|
+
libs_state: Dict[str, Any] = jobs_state.get("libs", {})
|
|
50
|
+
|
|
51
|
+
state_values = {
|
|
52
|
+
lib: (state["supported"], state["enabled"]) for lib, state in libs_state.items()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return python_exe, state_values
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def run_progress_get_state(
|
|
59
|
+
library: Optional[str] = None,
|
|
60
|
+
) -> Tuple[str, Dict[str, Tuple[bool, bool]]]:
|
|
61
|
+
"""Run get state function with rich progress UI."""
|
|
62
|
+
return run_progress_task(
|
|
63
|
+
get_state,
|
|
64
|
+
library,
|
|
65
|
+
description="Collecting package metadata...",
|
|
66
|
+
error_message=f"Failed to collect {library} package metadata.",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def handle_jobs_state(
|
|
71
|
+
library: str,
|
|
72
|
+
action: str, # 'enable' or 'disable'
|
|
73
|
+
action_callback: Callable[[], None],
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
|
|
76
|
+
_, state_values = run_progress_get_state(library)
|
|
77
|
+
installed, enabled = state_values[library]
|
|
78
|
+
|
|
79
|
+
if not installed:
|
|
80
|
+
handle_error(
|
|
81
|
+
message=f"{library} not installed."
|
|
82
|
+
) # TODO: Provide command to install library?
|
|
83
|
+
if (enabled and action == "enable") or (not enabled and action == "disable"):
|
|
84
|
+
action_color = "green" if enabled else "red"
|
|
85
|
+
console = Console()
|
|
86
|
+
console.print(
|
|
87
|
+
f"\nqBraid quantum jobs already [bold {action_color}]{action}d[/bold {action_color}] "
|
|
88
|
+
f"for [magenta]{library}[/magenta].\n\nCheck the state of all quantum jobs "
|
|
89
|
+
"libraries in this environment with: \n\n\t$ qbraid jobs state\n"
|
|
90
|
+
)
|
|
91
|
+
raise typer.Exit()
|
|
92
|
+
|
|
93
|
+
action_callback() # Perform the specific enable/disable action
|
qbraid_cli/kernels/__init__.py
CHANGED
qbraid_cli/kernels/app.py
CHANGED
|
@@ -1,42 +1,111 @@
|
|
|
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
|
"""
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
5
10
|
|
|
6
11
|
import typer
|
|
12
|
+
from jupyter_client.kernelspec import KernelSpecManager
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from qbraid_cli.envs.data_handling import installed_envs_data
|
|
16
|
+
from qbraid_cli.handlers import handle_error
|
|
17
|
+
|
|
18
|
+
kernels_app = typer.Typer(help="Manage qBraid kernels.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_kernels_path(environment: str) -> Path:
|
|
22
|
+
"""Get the path to the kernels directory for the given environment."""
|
|
23
|
+
slug_to_path, name_to_slug = installed_envs_data()
|
|
24
|
+
|
|
25
|
+
if environment in name_to_slug:
|
|
26
|
+
slug = name_to_slug.get(environment, None)
|
|
27
|
+
else:
|
|
28
|
+
slug = environment
|
|
7
29
|
|
|
8
|
-
|
|
30
|
+
if slug not in slug_to_path:
|
|
31
|
+
raise ValueError(f"Environment '{environment}' not found.")
|
|
9
32
|
|
|
33
|
+
env_path = slug_to_path[slug]
|
|
34
|
+
kernels_path = env_path / "kernels"
|
|
35
|
+
return kernels_path
|
|
10
36
|
|
|
11
|
-
|
|
37
|
+
|
|
38
|
+
@kernels_app.command(name="list")
|
|
12
39
|
def kernels_list():
|
|
13
40
|
"""List all available kernels."""
|
|
14
|
-
|
|
41
|
+
console = Console()
|
|
15
42
|
|
|
16
43
|
kernel_spec_manager = KernelSpecManager()
|
|
17
44
|
kernelspecs = kernel_spec_manager.get_all_specs()
|
|
18
45
|
|
|
19
46
|
if len(kernelspecs) == 0:
|
|
20
|
-
print("No qBraid kernels active.")
|
|
21
|
-
|
|
47
|
+
console.print("No qBraid kernels are active.")
|
|
48
|
+
console.print("\nUse 'qbraid kernels add' to add a new kernel.")
|
|
22
49
|
return
|
|
23
50
|
|
|
24
51
|
longest_kernel_name = max(len(kernel_name) for kernel_name in kernelspecs)
|
|
25
52
|
spacing = longest_kernel_name + 10
|
|
26
53
|
|
|
27
|
-
print("# qbraid kernels
|
|
28
|
-
print("#")
|
|
29
|
-
print("")
|
|
54
|
+
console.print("# qbraid kernels:\n#\n")
|
|
30
55
|
|
|
31
56
|
# Ensure 'python3' kernel is printed first if it exists
|
|
32
|
-
|
|
57
|
+
default_kernel_name = "python3"
|
|
58
|
+
python3_kernel_info = kernelspecs.pop(default_kernel_name, None)
|
|
33
59
|
if python3_kernel_info:
|
|
34
|
-
print("
|
|
60
|
+
console.print(f"{default_kernel_name.ljust(spacing)}{python3_kernel_info['resource_dir']}")
|
|
35
61
|
|
|
36
62
|
# Print the rest of the kernels
|
|
37
63
|
for kernel_name, kernel_info in sorted(kernelspecs.items()):
|
|
38
|
-
print(f"{kernel_name.ljust(spacing)}{kernel_info['resource_dir']}")
|
|
64
|
+
console.print(f"{kernel_name.ljust(spacing)}{kernel_info['resource_dir']}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@kernels_app.command(name="add")
|
|
68
|
+
def kernels_add(
|
|
69
|
+
environment: str = typer.Argument(
|
|
70
|
+
..., help="Name of environment for which to add ipykernel. Values from 'qbraid envs list'."
|
|
71
|
+
)
|
|
72
|
+
):
|
|
73
|
+
"""Add a kernel."""
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
kernels_path = _get_kernels_path(environment)
|
|
77
|
+
except ValueError:
|
|
78
|
+
handle_error(message=f"Environment '{environment}' not found.", include_traceback=False)
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
is_local = str(kernels_path).startswith(str(Path.home()))
|
|
82
|
+
resource_path = str(Path.home() / ".local") if is_local else sys.prefix
|
|
83
|
+
|
|
84
|
+
kernel_spec_manager = KernelSpecManager()
|
|
85
|
+
|
|
86
|
+
for kernel in kernels_path.iterdir():
|
|
87
|
+
kernel_spec_manager.install_kernel_spec(source_dir=str(kernel), prefix=resource_path)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@kernels_app.command(name="remove")
|
|
91
|
+
def kernels_remove(
|
|
92
|
+
environment: str = typer.Argument(
|
|
93
|
+
...,
|
|
94
|
+
help=("Name of environment for which to remove ipykernel. Values from 'qbraid envs list'."),
|
|
95
|
+
)
|
|
96
|
+
):
|
|
97
|
+
"""Remove a kernel."""
|
|
98
|
+
try:
|
|
99
|
+
kernels_path = _get_kernels_path(environment)
|
|
100
|
+
except ValueError:
|
|
101
|
+
handle_error(message=f"Environment '{environment}' not found.", include_traceback=False)
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
kernel_spec_manager = KernelSpecManager()
|
|
105
|
+
|
|
106
|
+
for kernel in kernels_path.iterdir():
|
|
107
|
+
kernel_spec_manager.remove_kernel_spec(kernel.name)
|
|
39
108
|
|
|
40
109
|
|
|
41
110
|
if __name__ == "__main__":
|
|
42
|
-
|
|
111
|
+
kernels_app()
|
qbraid_cli/main.py
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
1
4
|
"""
|
|
2
5
|
Entrypoint for the qBraid CLI.
|
|
3
6
|
|
|
4
7
|
"""
|
|
5
8
|
|
|
6
9
|
import typer
|
|
10
|
+
import urllib3
|
|
7
11
|
|
|
8
|
-
from .configure import
|
|
9
|
-
from .credits import
|
|
10
|
-
from .devices import
|
|
11
|
-
from .envs import
|
|
12
|
-
from .jobs import
|
|
13
|
-
from .kernels import
|
|
12
|
+
from qbraid_cli.configure.app import configure_app
|
|
13
|
+
from qbraid_cli.credits.app import credits_app
|
|
14
|
+
from qbraid_cli.devices.app import devices_app
|
|
15
|
+
from qbraid_cli.envs.app import envs_app
|
|
16
|
+
from qbraid_cli.jobs.app import jobs_app
|
|
17
|
+
from qbraid_cli.kernels.app import kernels_app
|
|
14
18
|
|
|
15
|
-
app = typer.Typer()
|
|
19
|
+
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
|
|
16
20
|
app.add_typer(configure_app, name="configure")
|
|
17
21
|
app.add_typer(envs_app, name="envs")
|
|
18
22
|
app.add_typer(jobs_app, name="jobs")
|
|
@@ -20,6 +24,8 @@ app.add_typer(devices_app, name="devices")
|
|
|
20
24
|
app.add_typer(kernels_app, name="kernels")
|
|
21
25
|
app.add_typer(credits_app, name="credits")
|
|
22
26
|
|
|
27
|
+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
28
|
+
|
|
23
29
|
|
|
24
30
|
def version_callback(value: bool):
|
|
25
31
|
"""Show the version and exit."""
|
|
@@ -50,7 +56,7 @@ def show_banner():
|
|
|
50
56
|
typer.echo("")
|
|
51
57
|
typer.echo("- Use 'qbraid --version' to see the current version.")
|
|
52
58
|
typer.echo("")
|
|
53
|
-
typer.echo("Reference Docs: https://docs.qbraid.com/projects/cli/en/
|
|
59
|
+
typer.echo("Reference Docs: https://docs.qbraid.com/projects/cli/en/stable/guide/overview.html")
|
|
54
60
|
|
|
55
61
|
|
|
56
62
|
@app.callback(invoke_without_command=True)
|
|
@@ -59,6 +65,7 @@ def main(
|
|
|
59
65
|
version: bool = typer.Option(
|
|
60
66
|
None,
|
|
61
67
|
"--version",
|
|
68
|
+
"-v",
|
|
62
69
|
callback=version_callback,
|
|
63
70
|
is_eager=True,
|
|
64
71
|
help="Show the version and exit.",
|