qbraid-cli 0.8.0.dev0__py3-none-any.whl → 0.8.0.dev3__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 -109
- 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 +162 -114
- qbraid_cli/envs/create.py +7 -14
- qbraid_cli/envs/data_handling.py +140 -0
- qbraid_cli/exceptions.py +3 -0
- qbraid_cli/handlers.py +4 -1
- qbraid_cli/jobs/__init__.py +4 -1
- qbraid_cli/jobs/app.py +80 -95
- qbraid_cli/jobs/toggle_braket.py +25 -23
- qbraid_cli/jobs/validation.py +74 -0
- qbraid_cli/kernels/__init__.py +4 -1
- qbraid_cli/kernels/app.py +93 -11
- qbraid_cli/main.py +15 -8
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.8.0.dev3.dist-info}/METADATA +58 -42
- qbraid_cli-0.8.0.dev3.dist-info/RECORD +29 -0
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.8.0.dev3.dist-info}/WHEEL +1 -1
- qbraid_cli-0.8.0.dev0.dist-info/RECORD +0 -25
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.8.0.dev3.dist-info}/entry_points.txt +0 -0
- {qbraid_cli-0.8.0.dev0.dist-info → qbraid_cli-0.8.0.dev3.dist-info}/top_level.txt +0 -0
qbraid_cli/jobs/app.py
CHANGED
|
@@ -1,118 +1,65 @@
|
|
|
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
|
-
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Any, Callable, Dict, Tuple
|
|
7
11
|
|
|
8
12
|
import typer
|
|
9
13
|
from rich.console import Console
|
|
10
14
|
|
|
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 = {}
|
|
28
|
-
|
|
29
|
-
if library:
|
|
30
|
-
libraries_to_check = [library]
|
|
31
|
-
else:
|
|
32
|
-
libraries_to_check = QJOB_LIBS
|
|
33
|
-
|
|
34
|
-
for lib in libraries_to_check:
|
|
35
|
-
state_values[lib] = qbraid_jobs_state(lib)
|
|
36
|
-
|
|
37
|
-
return state_values
|
|
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()
|
|
15
|
+
from qbraid_cli.handlers import handle_error, run_progress_task
|
|
16
|
+
from qbraid_cli.jobs.toggle_braket import disable_braket, enable_braket
|
|
17
|
+
from qbraid_cli.jobs.validation import handle_jobs_state, run_progress_get_state, validate_library
|
|
73
18
|
|
|
74
|
-
|
|
19
|
+
jobs_app = typer.Typer(help="Manage qBraid quantum jobs.")
|
|
75
20
|
|
|
76
21
|
|
|
77
|
-
@
|
|
22
|
+
@jobs_app.command(name="enable")
|
|
78
23
|
def jobs_enable(
|
|
79
24
|
library: str = typer.Argument(
|
|
80
25
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
81
|
-
)
|
|
26
|
+
),
|
|
27
|
+
auto_confirm: bool = typer.Option(
|
|
28
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
29
|
+
),
|
|
82
30
|
) -> None:
|
|
83
31
|
"""Enable qBraid Quantum Jobs."""
|
|
84
32
|
|
|
85
33
|
def enable_action():
|
|
86
34
|
if library == "braket":
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
enable_braket()
|
|
35
|
+
enable_braket(auto_confirm=auto_confirm)
|
|
90
36
|
else:
|
|
91
37
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
92
38
|
|
|
93
39
|
handle_jobs_state(library, "enable", enable_action)
|
|
94
40
|
|
|
95
41
|
|
|
96
|
-
@
|
|
42
|
+
@jobs_app.command(name="disable")
|
|
97
43
|
def jobs_disable(
|
|
98
44
|
library: str = typer.Argument(
|
|
99
45
|
..., help="Software library with quantum jobs support.", callback=validate_library
|
|
100
|
-
)
|
|
46
|
+
),
|
|
47
|
+
auto_confirm: bool = typer.Option(
|
|
48
|
+
False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
|
|
49
|
+
),
|
|
101
50
|
) -> None:
|
|
102
51
|
"""Disable qBraid Quantum Jobs."""
|
|
103
52
|
|
|
104
53
|
def disable_action():
|
|
105
54
|
if library == "braket":
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
disable_braket()
|
|
55
|
+
disable_braket(auto_confirm=auto_confirm)
|
|
109
56
|
else:
|
|
110
57
|
raise RuntimeError(f"Unsupported device library: '{library}'.")
|
|
111
58
|
|
|
112
59
|
handle_jobs_state(library, "disable", disable_action)
|
|
113
60
|
|
|
114
61
|
|
|
115
|
-
@
|
|
62
|
+
@jobs_app.command(name="state")
|
|
116
63
|
def jobs_state(
|
|
117
64
|
library: str = typer.Argument(
|
|
118
65
|
default=None,
|
|
@@ -122,23 +69,26 @@ def jobs_state(
|
|
|
122
69
|
) -> None:
|
|
123
70
|
"""Display the state of qBraid Quantum Jobs for the current environment."""
|
|
124
71
|
state_values: Dict[str, Tuple[bool, bool]] = run_progress_get_state(library)
|
|
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
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if enabled:
|
|
132
|
-
console.print(f"{lib:<12} ", "[green]enabled", end="")
|
|
133
|
-
else:
|
|
134
|
-
console.print(f"{lib:<12} ", "[red]disabled", end="")
|
|
135
|
-
else:
|
|
136
|
-
console.print(f"{lib:<12} n/a", end="")
|
|
79
|
+
console.print(f"Executable: {sys.executable}")
|
|
80
|
+
console.print(f"\n{header_1:<{padding}}{header_2}", style="bold")
|
|
137
81
|
|
|
138
|
-
|
|
82
|
+
for lib, (installed, enabled) in state_values.items():
|
|
83
|
+
state_str = (
|
|
84
|
+
"[green]enabled"
|
|
85
|
+
if enabled and installed
|
|
86
|
+
else "[red]disabled" if installed else "[grey70]unavailable"
|
|
87
|
+
)
|
|
88
|
+
console.print(f"{lib:<{padding-1}}", state_str, end="\n")
|
|
139
89
|
|
|
140
90
|
|
|
141
|
-
@
|
|
91
|
+
@jobs_app.command(name="list")
|
|
142
92
|
def jobs_list(
|
|
143
93
|
limit: int = typer.Option(
|
|
144
94
|
10, "--limit", "-l", help="Limit the maximum number of results returned"
|
|
@@ -146,20 +96,55 @@ def jobs_list(
|
|
|
146
96
|
) -> None:
|
|
147
97
|
"""List qBraid Quantum Jobs."""
|
|
148
98
|
|
|
149
|
-
def import_jobs() -> Tuple[
|
|
150
|
-
from
|
|
151
|
-
|
|
99
|
+
def import_jobs() -> Tuple[Any, Callable]:
|
|
100
|
+
from qbraid_core.services.quantum import QuantumClient, process_job_data
|
|
101
|
+
|
|
102
|
+
client = QuantumClient()
|
|
152
103
|
|
|
153
|
-
return
|
|
104
|
+
return client, process_job_data
|
|
154
105
|
|
|
155
|
-
result: Tuple[
|
|
156
|
-
|
|
106
|
+
result: Tuple[Any, Callable] = run_progress_task(import_jobs)
|
|
107
|
+
client, process_job_data = result
|
|
108
|
+
# https://github.com/qBraid/api/issues/644
|
|
109
|
+
# raw_data = client.search_jobs(query={"numResults": limit})
|
|
110
|
+
raw_data = client.search_jobs(query={})
|
|
111
|
+
job_data, msg = process_job_data(raw_data)
|
|
112
|
+
job_data = job_data[:limit]
|
|
157
113
|
|
|
114
|
+
longest_job_id = max(len(item[0]) for item in job_data)
|
|
115
|
+
spacing = longest_job_id + 5
|
|
158
116
|
try:
|
|
159
|
-
|
|
160
|
-
|
|
117
|
+
console = Console()
|
|
118
|
+
header_1 = "Job ID"
|
|
119
|
+
header_2 = "Submitted"
|
|
120
|
+
header_3 = "Status"
|
|
121
|
+
console.print(f"\n[bold]{header_1.ljust(spacing)}{header_2.ljust(36)}{header_3}[/bold]")
|
|
122
|
+
for job_id, submitted, status in job_data:
|
|
123
|
+
if status == "COMPLETED":
|
|
124
|
+
status_color = "green"
|
|
125
|
+
elif status in ["FAILED", "CANCELLED"]:
|
|
126
|
+
status_color = "red"
|
|
127
|
+
elif status in [
|
|
128
|
+
"INITIALIZING",
|
|
129
|
+
"INITIALIZED",
|
|
130
|
+
"CREATED",
|
|
131
|
+
"QUEUED",
|
|
132
|
+
"VALIDATING",
|
|
133
|
+
"RUNNING",
|
|
134
|
+
]:
|
|
135
|
+
status_color = "blue"
|
|
136
|
+
else:
|
|
137
|
+
status_color = "grey"
|
|
138
|
+
console.print(
|
|
139
|
+
f"{job_id.ljust(spacing)}{submitted.ljust(35)}",
|
|
140
|
+
f"[{status_color}]{status}[/{status_color}]",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
console.print(f"\n{msg}", style="italic", justify="left")
|
|
144
|
+
|
|
145
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
161
146
|
handle_error(message="Failed to fetch quantum jobs.")
|
|
162
147
|
|
|
163
148
|
|
|
164
149
|
if __name__ == "__main__":
|
|
165
|
-
|
|
150
|
+
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
|
|
|
@@ -11,6 +14,12 @@ from pathlib import Path
|
|
|
11
14
|
from typing import Optional, Tuple
|
|
12
15
|
|
|
13
16
|
import typer
|
|
17
|
+
from qbraid_core.system import (
|
|
18
|
+
QbraidSystemError,
|
|
19
|
+
get_active_site_packages_path,
|
|
20
|
+
get_latest_package_version,
|
|
21
|
+
get_local_package_version,
|
|
22
|
+
)
|
|
14
23
|
|
|
15
24
|
from qbraid_cli.exceptions import QbraidException
|
|
16
25
|
from qbraid_cli.handlers import handle_error, handle_filesystem_operation, run_progress_task
|
|
@@ -33,22 +42,16 @@ def get_package_data(package: str) -> Tuple[str, str, str]:
|
|
|
33
42
|
QbraidException: If package version or location data cannot be retrieved.
|
|
34
43
|
|
|
35
44
|
"""
|
|
36
|
-
from qbraid.api.system import (
|
|
37
|
-
get_active_site_packages_path,
|
|
38
|
-
get_latest_package_version,
|
|
39
|
-
get_local_package_version,
|
|
40
|
-
)
|
|
41
|
-
from qbraid.exceptions import QbraidError
|
|
42
45
|
|
|
43
46
|
try:
|
|
44
47
|
installed_version = get_local_package_version(package)
|
|
45
48
|
latest_version = get_latest_package_version(package)
|
|
46
|
-
except
|
|
49
|
+
except QbraidSystemError as err:
|
|
47
50
|
raise QbraidException("Failed to retrieve package version information") from err
|
|
48
51
|
|
|
49
52
|
try:
|
|
50
53
|
site_packages_path = get_active_site_packages_path()
|
|
51
|
-
except
|
|
54
|
+
except QbraidSystemError as err:
|
|
52
55
|
raise QbraidException("Failed to retrieve site-package location") from err
|
|
53
56
|
|
|
54
57
|
return installed_version, latest_version, site_packages_path
|
|
@@ -70,9 +73,6 @@ def confirm_updates(
|
|
|
70
73
|
installed_version (optional, str): The installed version of the target package.
|
|
71
74
|
latest_version (optional, str): The latest version of the target package available on PyPI.
|
|
72
75
|
|
|
73
|
-
Returns:
|
|
74
|
-
None
|
|
75
|
-
|
|
76
76
|
Raises:
|
|
77
77
|
ValueError: If an invalid mode is provided.
|
|
78
78
|
typer.Exit: If the user declines to proceed with enabling or disabling qBraid Quantum Jobs.
|
|
@@ -154,14 +154,16 @@ def aws_configure_dummy() -> None:
|
|
|
154
154
|
handle_error(message="Failed to configure qBraid quantum jobs.")
|
|
155
155
|
|
|
156
156
|
|
|
157
|
-
def enable_braket():
|
|
157
|
+
def enable_braket(auto_confirm: bool = False):
|
|
158
158
|
"""Enable qBraid quantum jobs for Amazon Braket."""
|
|
159
|
-
|
|
159
|
+
installed, latest, path = run_progress_task(
|
|
160
160
|
get_package_data, "boto3", description="Solving environment..."
|
|
161
161
|
)
|
|
162
|
-
confirm_updates("enable", site_packages_path, installed_version, latest_version)
|
|
163
|
-
aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
|
|
164
162
|
|
|
163
|
+
if not auto_confirm:
|
|
164
|
+
confirm_updates("enable", path, installed_version=installed, latest_version=latest)
|
|
165
|
+
|
|
166
|
+
aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
|
|
165
167
|
try:
|
|
166
168
|
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "boto3"])
|
|
167
169
|
subprocess.check_call(
|
|
@@ -174,19 +176,19 @@ def enable_braket():
|
|
|
174
176
|
handle_error(message="Failed to enable qBraid quantum jobs.")
|
|
175
177
|
|
|
176
178
|
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
|
-
)
|
|
179
|
+
typer.secho("\nTo disable, run: \n\n\t$ qbraid jobs disable braket\n")
|
|
180
180
|
|
|
181
181
|
|
|
182
|
-
def disable_braket():
|
|
182
|
+
def disable_braket(auto_confirm: bool = False):
|
|
183
183
|
"""Disable qBraid quantum jobs for Amazon Braket."""
|
|
184
184
|
package = "botocore"
|
|
185
|
-
|
|
185
|
+
installed, latest, path = run_progress_task(
|
|
186
186
|
get_package_data, package, description="Solving environment..."
|
|
187
187
|
)
|
|
188
|
-
package = f"{package}~={
|
|
189
|
-
|
|
188
|
+
package = f"{package}~={installed}" if installed < latest else package
|
|
189
|
+
|
|
190
|
+
if not auto_confirm:
|
|
191
|
+
confirm_updates("disable", path)
|
|
190
192
|
|
|
191
193
|
try:
|
|
192
194
|
subprocess.check_call(
|
|
@@ -204,4 +206,4 @@ def disable_braket():
|
|
|
204
206
|
handle_error(message="Failed to disable qBraid quantum jobs.")
|
|
205
207
|
|
|
206
208
|
typer.secho("\nSuccessfully disabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
|
|
207
|
-
typer.secho("\nTo enable, run:
|
|
209
|
+
typer.secho("\nTo enable, run: \n\n\t$ qbraid jobs enable braket\n")
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
|
|
9
|
+
from typing import Callable, Dict, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from qbraid_core.services.quantum.proxy import SUPPORTED_QJOB_LIBS, quantum_lib_proxy_state
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from qbraid_cli.handlers import handle_error, run_progress_task, validate_item
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_library(value: str) -> str:
|
|
19
|
+
"""Validate quantum jobs library."""
|
|
20
|
+
return validate_item(value, SUPPORTED_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
|
+
|
|
26
|
+
state_values = {}
|
|
27
|
+
|
|
28
|
+
if library:
|
|
29
|
+
libraries_to_check = [library]
|
|
30
|
+
else:
|
|
31
|
+
libraries_to_check = SUPPORTED_QJOB_LIBS
|
|
32
|
+
|
|
33
|
+
for lib in libraries_to_check:
|
|
34
|
+
state = quantum_lib_proxy_state(lib)
|
|
35
|
+
state_values[lib] = state["supported"], state["enabled"]
|
|
36
|
+
|
|
37
|
+
return state_values
|
|
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: \n\n\t$ qbraid jobs state\n"
|
|
71
|
+
)
|
|
72
|
+
raise typer.Exit()
|
|
73
|
+
|
|
74
|
+
action_callback() # Perform the specific enable/disable action
|
qbraid_cli/kernels/__init__.py
CHANGED
qbraid_cli/kernels/app.py
CHANGED
|
@@ -1,29 +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
|
"""
|
|
5
|
-
|
|
6
|
-
import
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
7
10
|
|
|
8
11
|
import typer
|
|
12
|
+
from jupyter_client.kernelspec import KernelSpecManager
|
|
13
|
+
from rich.console import Console
|
|
9
14
|
|
|
15
|
+
from qbraid_cli.envs.data_handling import installed_envs_data
|
|
10
16
|
from qbraid_cli.handlers import handle_error
|
|
11
17
|
|
|
12
|
-
|
|
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
|
|
29
|
+
|
|
30
|
+
if slug not in slug_to_path:
|
|
31
|
+
raise ValueError(f"Environment '{environment}' not found.")
|
|
13
32
|
|
|
33
|
+
env_path = slug_to_path[slug]
|
|
34
|
+
kernels_path = env_path / "kernels"
|
|
35
|
+
return kernels_path
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
|
|
38
|
+
@kernels_app.command(name="list")
|
|
16
39
|
def kernels_list():
|
|
17
40
|
"""List all available kernels."""
|
|
41
|
+
console = Console()
|
|
42
|
+
|
|
43
|
+
kernel_spec_manager = KernelSpecManager()
|
|
44
|
+
kernelspecs = kernel_spec_manager.get_all_specs()
|
|
45
|
+
|
|
46
|
+
if len(kernelspecs) == 0:
|
|
47
|
+
console.print("No qBraid kernels are active.")
|
|
48
|
+
console.print("\nUse 'qbraid kernels add' to add a new kernel.")
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
longest_kernel_name = max(len(kernel_name) for kernel_name in kernelspecs)
|
|
52
|
+
spacing = longest_kernel_name + 10
|
|
53
|
+
|
|
54
|
+
console.print("# qbraid kernels:\n#\n")
|
|
55
|
+
|
|
56
|
+
# Ensure 'python3' kernel is printed first if it exists
|
|
57
|
+
default_kernel_name = "python3"
|
|
58
|
+
python3_kernel_info = kernelspecs.pop(default_kernel_name, None)
|
|
59
|
+
if python3_kernel_info:
|
|
60
|
+
console.print(f"{default_kernel_name.ljust(spacing)}{python3_kernel_info['resource_dir']}")
|
|
61
|
+
|
|
62
|
+
# Print the rest of the kernels
|
|
63
|
+
for kernel_name, kernel_info in sorted(kernelspecs.items()):
|
|
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
|
+
|
|
18
75
|
try:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
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()
|
|
22
105
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
handle_error("Failed to list kernels.")
|
|
106
|
+
for kernel in kernels_path.iterdir():
|
|
107
|
+
kernel_spec_manager.remove_kernel_spec(kernel.name)
|
|
26
108
|
|
|
27
109
|
|
|
28
110
|
if __name__ == "__main__":
|
|
29
|
-
|
|
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.",
|