qbraid-cli 0.8.0.dev3__py3-none-any.whl → 0.8.0.dev5__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/admin/__init__.py +9 -0
- qbraid_cli/admin/app.py +50 -0
- qbraid_cli/admin/headers.py +193 -0
- qbraid_cli/admin/validation.py +33 -0
- qbraid_cli/devices/validation.py +0 -1
- qbraid_cli/handlers.py +5 -1
- qbraid_cli/jobs/app.py +3 -4
- qbraid_cli/jobs/toggle_braket.py +29 -53
- qbraid_cli/jobs/validation.py +41 -22
- qbraid_cli/main.py +5 -5
- {qbraid_cli-0.8.0.dev3.dist-info → qbraid_cli-0.8.0.dev5.dist-info}/METADATA +6 -3
- {qbraid_cli-0.8.0.dev3.dist-info → qbraid_cli-0.8.0.dev5.dist-info}/RECORD +16 -12
- {qbraid_cli-0.8.0.dev3.dist-info → qbraid_cli-0.8.0.dev5.dist-info}/WHEEL +0 -0
- {qbraid_cli-0.8.0.dev3.dist-info → qbraid_cli-0.8.0.dev5.dist-info}/entry_points.txt +0 -0
- {qbraid_cli-0.8.0.dev3.dist-info → qbraid_cli-0.8.0.dev5.dist-info}/top_level.txt +0 -0
qbraid_cli/_version.py
CHANGED
|
@@ -12,5 +12,5 @@ __version__: str
|
|
|
12
12
|
__version_tuple__: VERSION_TUPLE
|
|
13
13
|
version_tuple: VERSION_TUPLE
|
|
14
14
|
|
|
15
|
-
__version__ = version = '0.8.0.
|
|
16
|
-
__version_tuple__ = version_tuple = (0, 8, 0, '
|
|
15
|
+
__version__ = version = '0.8.0.dev5'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 8, 0, 'dev5')
|
qbraid_cli/admin/app.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Module defining commands in the 'qbraid admin' namespace.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from qbraid_cli.admin.headers import check_and_fix_headers
|
|
14
|
+
from qbraid_cli.admin.validation import validate_header_type, validate_paths_exist
|
|
15
|
+
|
|
16
|
+
# disable pretty_exceptions_show_locals to avoid printing sensative information in the traceback
|
|
17
|
+
admin_app = typer.Typer(
|
|
18
|
+
help="CI/CD commands for qBraid maintainers.",
|
|
19
|
+
pretty_exceptions_show_locals=False,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@admin_app.command(name="headers")
|
|
24
|
+
def admin_headers(
|
|
25
|
+
src_paths: List[str] = typer.Argument(
|
|
26
|
+
..., help="Source file or directory paths to verify.", callback=validate_paths_exist
|
|
27
|
+
),
|
|
28
|
+
header_type: str = typer.Option(
|
|
29
|
+
"default",
|
|
30
|
+
"--type",
|
|
31
|
+
"-t",
|
|
32
|
+
help="Type of header to use ('default' or 'gpl').",
|
|
33
|
+
callback=validate_header_type,
|
|
34
|
+
),
|
|
35
|
+
skip_files: List[str] = typer.Option(
|
|
36
|
+
[], "--skip", "-s", help="Files to skip during verification.", callback=validate_paths_exist
|
|
37
|
+
),
|
|
38
|
+
fix: bool = typer.Option(
|
|
39
|
+
False, "--fix", "-f", help="Whether to fix the headers instead of just verifying."
|
|
40
|
+
),
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Verifies and optionally fixes qBraid headers in specified files and directories.
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
check_and_fix_headers(src_paths, header_type=header_type, skip_files=skip_files, fix=fix)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
admin_app()
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Script to verify qBraid copyright file headers
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from typing import List, Optional
|
|
11
|
+
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
|
|
14
|
+
# pylint: disable=too-many-branches,too-many-statements
|
|
15
|
+
|
|
16
|
+
DEFAULT_HEADER = """# Copyright (c) 2024, qBraid Development Team
|
|
17
|
+
# All rights reserved.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
DEFAULT_HEADER_GPL = """# Copyright (C) 2024 qBraid
|
|
21
|
+
#
|
|
22
|
+
# This file is part of the qBraid-SDK
|
|
23
|
+
#
|
|
24
|
+
# The qBraid-SDK is free software released under the GNU General Public License v3
|
|
25
|
+
# or later. You can redistribute and/or modify it under the terms of the GPL v3.
|
|
26
|
+
# See the LICENSE file in the project root or <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
27
|
+
#
|
|
28
|
+
# THERE IS NO WARRANTY for the qBraid-SDK, as per Section 15 of the GPL v3.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
HEADER_TYPES = {
|
|
32
|
+
"default": DEFAULT_HEADER,
|
|
33
|
+
"gpl": DEFAULT_HEADER_GPL,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def check_and_fix_headers(
|
|
38
|
+
src_paths: List[str],
|
|
39
|
+
header_type: str = "default",
|
|
40
|
+
skip_files: Optional[List[str]] = None,
|
|
41
|
+
fix: bool = False,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Script to add or verify qBraid copyright file headers"""
|
|
44
|
+
try:
|
|
45
|
+
header = HEADER_TYPES[header_type]
|
|
46
|
+
except KeyError as err:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"Invalid header type: {HEADER_TYPES}. Expected one of {list(HEADER_TYPES.keys())}"
|
|
49
|
+
) from err
|
|
50
|
+
|
|
51
|
+
for path in src_paths:
|
|
52
|
+
if not os.path.exists(path):
|
|
53
|
+
sys.stderr.write(
|
|
54
|
+
f"Usage: qbraid verify_headers [OPTIONS] SRC ...\n"
|
|
55
|
+
f"Try 'qbraid verify_headers --help' for help.\n\n"
|
|
56
|
+
f"Error: Invalid value for 'SRC ...': Path '{path}' does not exist.\n"
|
|
57
|
+
)
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
header_2023 = header.replace("2024", "2023")
|
|
61
|
+
|
|
62
|
+
skip_files = skip_files or []
|
|
63
|
+
|
|
64
|
+
failed_headers = []
|
|
65
|
+
fixed_headers = []
|
|
66
|
+
|
|
67
|
+
console = Console()
|
|
68
|
+
|
|
69
|
+
def should_skip(file_path: str, content: str) -> bool:
|
|
70
|
+
if file_path in skip_files:
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
if os.path.basename(file_path) == "__init__.py":
|
|
74
|
+
return not content.strip()
|
|
75
|
+
|
|
76
|
+
skip_header_tag = "# qbraid: skip-header"
|
|
77
|
+
line_number = 0
|
|
78
|
+
|
|
79
|
+
for line in content.splitlines():
|
|
80
|
+
line_number += 1
|
|
81
|
+
if 5 <= line_number <= 30 and skip_header_tag in line:
|
|
82
|
+
return True
|
|
83
|
+
if line_number > 30:
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
def replace_or_add_header(file_path: str, fix: bool = False) -> None:
|
|
89
|
+
with open(file_path, "r", encoding="ISO-8859-1") as f:
|
|
90
|
+
content = f.read()
|
|
91
|
+
|
|
92
|
+
# This finds the start of the actual content after skipping initial whitespace and comments.
|
|
93
|
+
lines = content.splitlines()
|
|
94
|
+
first_non_comment_line_index = next(
|
|
95
|
+
(i for i, line in enumerate(lines) if not line.strip().startswith("#")), None
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Prepare the content by stripping leading and trailing whitespace and separating into lines
|
|
99
|
+
actual_content = (
|
|
100
|
+
"\n".join(lines[first_non_comment_line_index:]).strip()
|
|
101
|
+
if first_non_comment_line_index is not None
|
|
102
|
+
else ""
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Check if the content already starts with the header or if the file should be skipped
|
|
106
|
+
if (
|
|
107
|
+
content.lstrip().startswith(header)
|
|
108
|
+
or content.lstrip().startswith(header_2023)
|
|
109
|
+
or should_skip(file_path, content)
|
|
110
|
+
):
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
if not fix:
|
|
114
|
+
failed_headers.append(file_path)
|
|
115
|
+
else:
|
|
116
|
+
# Form the new content by combining the header, one blank line, and the actual content
|
|
117
|
+
new_content = header.strip() + "\n\n" + actual_content
|
|
118
|
+
with open(file_path, "w", encoding="ISO-8859-1") as f:
|
|
119
|
+
f.write(new_content)
|
|
120
|
+
fixed_headers.append(file_path)
|
|
121
|
+
|
|
122
|
+
def process_files_in_directory(directory: str, fix: bool = False) -> int:
|
|
123
|
+
count = 0
|
|
124
|
+
if not os.path.isdir(directory):
|
|
125
|
+
return count
|
|
126
|
+
for root, _, files in os.walk(directory):
|
|
127
|
+
for file in files:
|
|
128
|
+
if file.endswith(".py"):
|
|
129
|
+
file_path = os.path.join(root, file)
|
|
130
|
+
replace_or_add_header(file_path, fix)
|
|
131
|
+
count += 1
|
|
132
|
+
return count
|
|
133
|
+
|
|
134
|
+
checked = 0
|
|
135
|
+
for item in src_paths:
|
|
136
|
+
if os.path.isdir(item):
|
|
137
|
+
checked += process_files_in_directory(item, fix)
|
|
138
|
+
elif os.path.isfile(item) and item.endswith(".py"):
|
|
139
|
+
replace_or_add_header(item, fix)
|
|
140
|
+
checked += 1
|
|
141
|
+
else:
|
|
142
|
+
failed_headers.append(item)
|
|
143
|
+
print(f"File or directory not found: {item}")
|
|
144
|
+
|
|
145
|
+
if not fix:
|
|
146
|
+
if failed_headers:
|
|
147
|
+
for file in failed_headers:
|
|
148
|
+
console.print(f"[bold]would fix {file}[/bold]")
|
|
149
|
+
num_failed = len(failed_headers)
|
|
150
|
+
num_passed = checked - num_failed
|
|
151
|
+
s1, s2 = ("", "s") if num_failed == 1 else ("s", "")
|
|
152
|
+
s_passed = "" if num_passed == 1 else "s"
|
|
153
|
+
console.print("[bold]\nOh no![/bold] 💥 💔 💥")
|
|
154
|
+
if num_passed > 0:
|
|
155
|
+
punc = ", "
|
|
156
|
+
passed_msg = f"[blue]{num_passed}[/blue] file{s_passed} would be left unchanged."
|
|
157
|
+
else:
|
|
158
|
+
punc = "."
|
|
159
|
+
passed_msg = ""
|
|
160
|
+
|
|
161
|
+
failed_msg = f"[bold][blue]{num_failed}[/blue] file{s1} need{s2} updating{punc}[/bold]"
|
|
162
|
+
console.print(f"{failed_msg}{passed_msg}")
|
|
163
|
+
elif checked == 0:
|
|
164
|
+
console.print("[bold]No Python files present. Nothing to do[/bold] 😴")
|
|
165
|
+
else:
|
|
166
|
+
s_checked = "" if checked == 1 else "s"
|
|
167
|
+
console.print("[bold]All done![/bold] ✨ 🚀 ✨")
|
|
168
|
+
console.print(f"[blue]{checked}[/blue] file{s_checked} would be left unchanged.")
|
|
169
|
+
|
|
170
|
+
else:
|
|
171
|
+
for file in fixed_headers:
|
|
172
|
+
console.print(f"[bold]fixed {file}[/bold]")
|
|
173
|
+
num_fixed = len(fixed_headers)
|
|
174
|
+
num_ok = checked - num_fixed
|
|
175
|
+
s_fixed = "" if num_fixed == 1 else "s"
|
|
176
|
+
s_ok = "" if num_ok == 1 else "s"
|
|
177
|
+
console.print("\n[bold]All done![/bold] ✨ 🚀 ✨")
|
|
178
|
+
if num_ok > 0:
|
|
179
|
+
punc = ", "
|
|
180
|
+
unchanged_msg = f"[blue]{num_ok}[/blue] file{s_ok} left unchanged."
|
|
181
|
+
else:
|
|
182
|
+
punc = "."
|
|
183
|
+
unchanged_msg = ""
|
|
184
|
+
|
|
185
|
+
if num_fixed > 0:
|
|
186
|
+
fixed_msg = f"[bold][blue]{num_fixed}[/blue] file{s_fixed} fixed{punc}[/bold]"
|
|
187
|
+
else:
|
|
188
|
+
fixed_msg = ""
|
|
189
|
+
|
|
190
|
+
if fixed_msg or unchanged_msg:
|
|
191
|
+
console.print(f"{fixed_msg}{unchanged_msg}")
|
|
192
|
+
else:
|
|
193
|
+
console.print("[bold]No Python files present. Nothing to do[/bold] 😴")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2024, qBraid Development Team
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Module for validating command arguments for qBraid admin commands.
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
import typer
|
|
13
|
+
|
|
14
|
+
from qbraid_cli.admin.headers import HEADER_TYPES
|
|
15
|
+
from qbraid_cli.handlers import _format_list_items, validate_item
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_header_type(value: str) -> str:
|
|
19
|
+
"""Validate header type."""
|
|
20
|
+
header_types = list(HEADER_TYPES.keys())
|
|
21
|
+
return validate_item(value, header_types, "Header type")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def validate_paths_exist(paths: List[str]) -> List[str]:
|
|
25
|
+
"""Verifies that each path in the provided list exists."""
|
|
26
|
+
non_existent_paths = [path for path in paths if not os.path.exists(path)]
|
|
27
|
+
if non_existent_paths:
|
|
28
|
+
if len(non_existent_paths) == 1:
|
|
29
|
+
raise typer.BadParameter(f"Path '{non_existent_paths[0]}' does not exist")
|
|
30
|
+
raise typer.BadParameter(
|
|
31
|
+
f"The following paths do not exist: {_format_list_items(non_existent_paths)}"
|
|
32
|
+
)
|
|
33
|
+
return paths
|
qbraid_cli/devices/validation.py
CHANGED
qbraid_cli/handlers.py
CHANGED
|
@@ -44,7 +44,11 @@ def handle_error(
|
|
|
44
44
|
full_message = f"\n{error_prefix} {message}\n"
|
|
45
45
|
if include_traceback:
|
|
46
46
|
tb_string = traceback.format_exc()
|
|
47
|
-
|
|
47
|
+
# TODO: find out reason for weird traceback emitted from
|
|
48
|
+
# qbraid jobs enable/disable when library not installed.
|
|
49
|
+
# For now, if matches, just don't print it.
|
|
50
|
+
if tb_string.strip() != "NoneType: None":
|
|
51
|
+
full_message += f"\n{tb_string}"
|
|
48
52
|
typer.echo(full_message, err=True)
|
|
49
53
|
raise typer.Exit(code=1)
|
|
50
54
|
|
qbraid_cli/jobs/app.py
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
Module defining commands in the 'qbraid jobs' namespace.
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
import sys
|
|
10
8
|
from typing import Any, Callable, Dict, Tuple
|
|
11
9
|
|
|
12
10
|
import typer
|
|
@@ -68,7 +66,8 @@ def jobs_state(
|
|
|
68
66
|
)
|
|
69
67
|
) -> None:
|
|
70
68
|
"""Display the state of qBraid Quantum Jobs for the current environment."""
|
|
71
|
-
|
|
69
|
+
result: Tuple[str, Dict[str, Tuple[bool, bool]]] = run_progress_get_state(library)
|
|
70
|
+
python_exe, state_values = result
|
|
72
71
|
state_values = dict(sorted(state_values.items()))
|
|
73
72
|
|
|
74
73
|
console = Console()
|
|
@@ -76,7 +75,7 @@ def jobs_state(
|
|
|
76
75
|
max_lib_length = max((len(lib) for lib in state_values.keys()), default=len(header_1))
|
|
77
76
|
padding = max_lib_length + 9
|
|
78
77
|
|
|
79
|
-
console.print(f"Executable: {
|
|
78
|
+
console.print(f"Executable: {python_exe}")
|
|
80
79
|
console.print(f"\n{header_1:<{padding}}{header_2}", style="bold")
|
|
81
80
|
|
|
82
81
|
for lib, (installed, enabled) in state_values.items():
|
qbraid_cli/jobs/toggle_braket.py
CHANGED
|
@@ -9,17 +9,10 @@ Module supporting 'qbraid jobs enable/disable braket' and commands.
|
|
|
9
9
|
import logging
|
|
10
10
|
import os
|
|
11
11
|
import subprocess
|
|
12
|
-
import sys
|
|
13
12
|
from pathlib import Path
|
|
14
13
|
from typing import Optional, Tuple
|
|
15
14
|
|
|
16
15
|
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
|
-
)
|
|
23
16
|
|
|
24
17
|
from qbraid_cli.exceptions import QbraidException
|
|
25
18
|
from qbraid_cli.handlers import handle_error, handle_filesystem_operation, run_progress_task
|
|
@@ -28,33 +21,38 @@ logging.basicConfig(level=logging.INFO)
|
|
|
28
21
|
logger = logging.getLogger(__name__)
|
|
29
22
|
|
|
30
23
|
|
|
31
|
-
def get_package_data(package: str) -> Tuple[str, str, str]:
|
|
24
|
+
def get_package_data(package: str) -> Tuple[str, str, str, str]:
|
|
32
25
|
"""Retrieve package version and location data.
|
|
33
26
|
|
|
34
27
|
Args:
|
|
35
28
|
package (str): The name of the package to retrieve data for.
|
|
36
29
|
|
|
37
30
|
Returns:
|
|
38
|
-
Tuple[str, str, str]: The installed and latest versions of the package, and the
|
|
39
|
-
|
|
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.
|
|
40
33
|
|
|
41
34
|
Raises:
|
|
42
35
|
QbraidException: If package version or location data cannot be retrieved.
|
|
43
36
|
|
|
44
37
|
"""
|
|
38
|
+
# pylint: disable=import-outside-toplevel
|
|
39
|
+
from qbraid_core.system import (
|
|
40
|
+
QbraidSystemError,
|
|
41
|
+
get_active_python_path,
|
|
42
|
+
get_active_site_packages_path,
|
|
43
|
+
get_latest_package_version,
|
|
44
|
+
get_local_package_version,
|
|
45
|
+
)
|
|
45
46
|
|
|
46
47
|
try:
|
|
47
|
-
|
|
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)
|
|
48
51
|
latest_version = get_latest_package_version(package)
|
|
49
52
|
except QbraidSystemError as err:
|
|
50
|
-
raise QbraidException("Failed to retrieve package
|
|
51
|
-
|
|
52
|
-
try:
|
|
53
|
-
site_packages_path = get_active_site_packages_path()
|
|
54
|
-
except QbraidSystemError as err:
|
|
55
|
-
raise QbraidException("Failed to retrieve site-package location") from err
|
|
53
|
+
raise QbraidException("Failed to retrieve required system and/or package metadata") from err
|
|
56
54
|
|
|
57
|
-
return installed_version, latest_version, site_packages_path
|
|
55
|
+
return installed_version, latest_version, str(site_packages_path), str(python_pathlib)
|
|
58
56
|
|
|
59
57
|
|
|
60
58
|
def confirm_updates(
|
|
@@ -117,62 +115,39 @@ 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
|
"""
|
|
125
121
|
Initializes AWS configuration and credentials files with placeholder values.
|
|
126
122
|
|
|
127
|
-
This function ensures the existence of AWS config and credentials files in the user's home
|
|
128
|
-
directory. If these files do not already exist, it creates them and populates them with
|
|
129
|
-
placeholder values for the AWS access key and secret access key. While AWS credentials are not
|
|
130
|
-
required when submitting quantum tasks through qBraid, Amazon Braket requires these files to be
|
|
131
|
-
present to prevent configuration errors.
|
|
132
123
|
"""
|
|
133
|
-
|
|
134
|
-
config_path = aws_dir / "config"
|
|
135
|
-
credentials_path = aws_dir / "credentials"
|
|
136
|
-
|
|
137
|
-
def configure_aws():
|
|
138
|
-
aws_dir.mkdir(exist_ok=True)
|
|
139
|
-
if not config_path.exists():
|
|
140
|
-
config_content = "[default]\nregion = us-east-1\noutput = json\n"
|
|
141
|
-
config_path.write_text(config_content)
|
|
142
|
-
if not credentials_path.exists():
|
|
143
|
-
access_key, secret_key = "MYACCESSKEY", "MYSECRETKEY"
|
|
144
|
-
credentials_content = (
|
|
145
|
-
f"[default]\n"
|
|
146
|
-
f"aws_access_key_id = {access_key}\n"
|
|
147
|
-
f"aws_secret_access_key = {secret_key}\n"
|
|
148
|
-
)
|
|
149
|
-
credentials_path.write_text(credentials_content)
|
|
124
|
+
from qbraid_core.services.quantum.proxy_braket import aws_configure
|
|
150
125
|
|
|
151
126
|
try:
|
|
152
|
-
handle_filesystem_operation(
|
|
127
|
+
handle_filesystem_operation(aws_configure, Path.home() / ".aws")
|
|
153
128
|
except QbraidException:
|
|
154
129
|
handle_error(message="Failed to configure qBraid quantum jobs.")
|
|
155
130
|
|
|
156
131
|
|
|
157
132
|
def enable_braket(auto_confirm: bool = False):
|
|
158
133
|
"""Enable qBraid quantum jobs for Amazon Braket."""
|
|
159
|
-
installed, latest, path = run_progress_task(
|
|
134
|
+
installed, latest, path, python_exe = run_progress_task(
|
|
160
135
|
get_package_data, "boto3", description="Solving environment..."
|
|
161
136
|
)
|
|
162
137
|
|
|
163
138
|
if not auto_confirm:
|
|
164
139
|
confirm_updates("enable", path, installed_version=installed, latest_version=latest)
|
|
140
|
+
typer.echo("")
|
|
165
141
|
|
|
166
142
|
aws_configure_dummy() # TODO: possibly add another confirmation for writing aws config files
|
|
143
|
+
|
|
167
144
|
try:
|
|
168
|
-
subprocess.check_call([
|
|
145
|
+
subprocess.check_call([python_exe, "-m", "pip", "install", "--upgrade", "boto3"])
|
|
146
|
+
subprocess.check_call([python_exe, "-m", "pip", "uninstall", "botocore", "-y", "--quiet"])
|
|
169
147
|
subprocess.check_call(
|
|
170
|
-
[
|
|
148
|
+
[python_exe, "-m", "pip", "install", "git+https://github.com/qBraid/botocore.git"]
|
|
171
149
|
)
|
|
172
|
-
|
|
173
|
-
[sys.executable, "-m", "pip", "install", "git+https://github.com/qBraid/botocore.git"]
|
|
174
|
-
)
|
|
175
|
-
except subprocess.CalledProcessError:
|
|
150
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
176
151
|
handle_error(message="Failed to enable qBraid quantum jobs.")
|
|
177
152
|
|
|
178
153
|
typer.secho("\nSuccessfully enabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
|
|
@@ -182,18 +157,19 @@ def enable_braket(auto_confirm: bool = False):
|
|
|
182
157
|
def disable_braket(auto_confirm: bool = False):
|
|
183
158
|
"""Disable qBraid quantum jobs for Amazon Braket."""
|
|
184
159
|
package = "botocore"
|
|
185
|
-
installed, latest, path = run_progress_task(
|
|
160
|
+
installed, latest, path, python_exe = run_progress_task(
|
|
186
161
|
get_package_data, package, description="Solving environment..."
|
|
187
162
|
)
|
|
188
163
|
package = f"{package}~={installed}" if installed < latest else package
|
|
189
164
|
|
|
190
165
|
if not auto_confirm:
|
|
191
166
|
confirm_updates("disable", path)
|
|
167
|
+
typer.echo("")
|
|
192
168
|
|
|
193
169
|
try:
|
|
194
170
|
subprocess.check_call(
|
|
195
171
|
[
|
|
196
|
-
|
|
172
|
+
python_exe,
|
|
197
173
|
"-m",
|
|
198
174
|
"pip",
|
|
199
175
|
"install",
|
|
@@ -202,7 +178,7 @@ def disable_braket(auto_confirm: bool = False):
|
|
|
202
178
|
],
|
|
203
179
|
stderr=subprocess.DEVNULL,
|
|
204
180
|
)
|
|
205
|
-
except subprocess.CalledProcessError:
|
|
181
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
206
182
|
handle_error(message="Failed to disable qBraid quantum jobs.")
|
|
207
183
|
|
|
208
184
|
typer.secho("\nSuccessfully disabled qBraid quantum jobs.", fg=typer.colors.GREEN, bold=True)
|
qbraid_cli/jobs/validation.py
CHANGED
|
@@ -5,39 +5,59 @@
|
|
|
5
5
|
Module for validating command arguments for qBraid Quantum Jobs.
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
-
|
|
9
|
-
from typing import Callable, Dict, Optional, Tuple
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
import typer
|
|
12
|
-
from qbraid_core.services.quantum.proxy import SUPPORTED_QJOB_LIBS, quantum_lib_proxy_state
|
|
13
12
|
from rich.console import Console
|
|
14
13
|
|
|
15
14
|
from qbraid_cli.handlers import handle_error, run_progress_task, validate_item
|
|
16
15
|
|
|
16
|
+
LEGACY_ARGS: Dict[str, str] = {
|
|
17
|
+
"amazon_braket": "braket",
|
|
18
|
+
"aws_braket": "braket",
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
def validate_library(value: str) -> str:
|
|
19
23
|
"""Validate quantum jobs library."""
|
|
20
|
-
|
|
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())
|
|
21
28
|
|
|
29
|
+
if value in LEGACY_ARGS:
|
|
30
|
+
old_value = value
|
|
31
|
+
value = LEGACY_ARGS[value]
|
|
22
32
|
|
|
23
|
-
|
|
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]]]:
|
|
24
43
|
"""Get the state of qBraid Quantum Jobs for the specified library."""
|
|
44
|
+
from qbraid_core.services.quantum import QuantumClient
|
|
25
45
|
|
|
26
|
-
|
|
46
|
+
jobs_state = QuantumClient.qbraid_jobs_state(device_lib=library)
|
|
27
47
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
else:
|
|
31
|
-
libraries_to_check = SUPPORTED_QJOB_LIBS
|
|
48
|
+
python_exe: str = jobs_state.get("exe", sys.executable)
|
|
49
|
+
libs_state: Dict[str, Any] = jobs_state.get("libs", {})
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
state
|
|
35
|
-
|
|
51
|
+
state_values = {
|
|
52
|
+
lib: (state["supported"], state["enabled"]) for lib, state in libs_state.items()
|
|
53
|
+
}
|
|
36
54
|
|
|
37
|
-
return state_values
|
|
55
|
+
return python_exe, state_values
|
|
38
56
|
|
|
39
57
|
|
|
40
|
-
def run_progress_get_state(
|
|
58
|
+
def run_progress_get_state(
|
|
59
|
+
library: Optional[str] = None,
|
|
60
|
+
) -> Tuple[str, Dict[str, Tuple[bool, bool]]]:
|
|
41
61
|
"""Run get state function with rich progress UI."""
|
|
42
62
|
return run_progress_task(
|
|
43
63
|
get_state,
|
|
@@ -53,21 +73,20 @@ def handle_jobs_state(
|
|
|
53
73
|
action_callback: Callable[[], None],
|
|
54
74
|
) -> None:
|
|
55
75
|
"""Handle the common logic for enabling or disabling qBraid Quantum Jobs."""
|
|
56
|
-
|
|
76
|
+
_, state_values = run_progress_get_state(library)
|
|
57
77
|
installed, enabled = state_values[library]
|
|
58
78
|
|
|
59
79
|
if not installed:
|
|
60
|
-
handle_error(
|
|
80
|
+
handle_error(
|
|
81
|
+
message=f"{library} not installed."
|
|
82
|
+
) # TODO: Provide command to install library?
|
|
61
83
|
if (enabled and action == "enable") or (not enabled and action == "disable"):
|
|
62
84
|
action_color = "green" if enabled else "red"
|
|
63
85
|
console = Console()
|
|
64
86
|
console.print(
|
|
65
87
|
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"
|
|
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"
|
|
71
90
|
)
|
|
72
91
|
raise typer.Exit()
|
|
73
92
|
|
qbraid_cli/main.py
CHANGED
|
@@ -7,8 +7,8 @@ Entrypoint for the qBraid CLI.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
|
-
import urllib3
|
|
11
10
|
|
|
11
|
+
from qbraid_cli.admin.app import admin_app
|
|
12
12
|
from qbraid_cli.configure.app import configure_app
|
|
13
13
|
from qbraid_cli.credits.app import credits_app
|
|
14
14
|
from qbraid_cli.devices.app import devices_app
|
|
@@ -17,14 +17,14 @@ from qbraid_cli.jobs.app import jobs_app
|
|
|
17
17
|
from qbraid_cli.kernels.app import kernels_app
|
|
18
18
|
|
|
19
19
|
app = typer.Typer(context_settings={"help_option_names": ["-h", "--help"]})
|
|
20
|
+
|
|
21
|
+
app.add_typer(admin_app, name="admin")
|
|
20
22
|
app.add_typer(configure_app, name="configure")
|
|
23
|
+
app.add_typer(credits_app, name="credits")
|
|
24
|
+
app.add_typer(devices_app, name="devices")
|
|
21
25
|
app.add_typer(envs_app, name="envs")
|
|
22
26
|
app.add_typer(jobs_app, name="jobs")
|
|
23
|
-
app.add_typer(devices_app, name="devices")
|
|
24
27
|
app.add_typer(kernels_app, name="kernels")
|
|
25
|
-
app.add_typer(credits_app, name="credits")
|
|
26
|
-
|
|
27
|
-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
def version_callback(value: bool):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qbraid-cli
|
|
3
|
-
Version: 0.8.0.
|
|
3
|
+
Version: 0.8.0.dev5
|
|
4
4
|
Summary: Command Line Interface for interacting with all parts of the qBraid platform.
|
|
5
5
|
Author-email: qBraid Development Team <contact@qbraid.com>
|
|
6
6
|
License: Proprietary
|
|
@@ -29,7 +29,8 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
Requires-Dist: typer >=0.12.1
|
|
30
30
|
Requires-Dist: rich >=10.11.0
|
|
31
31
|
Requires-Dist: jupyter-client <9.0.0,>=7.0.0
|
|
32
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: ipykernel
|
|
33
|
+
Requires-Dist: qbraid-core >=0.1.3
|
|
33
34
|
Provides-Extra: dev
|
|
34
35
|
Requires-Dist: black ; extra == 'dev'
|
|
35
36
|
Requires-Dist: isort ; extra == 'dev'
|
|
@@ -38,9 +39,11 @@ Requires-Dist: pytest ; extra == 'dev'
|
|
|
38
39
|
Provides-Extra: docs
|
|
39
40
|
Requires-Dist: sphinx ~=7.2.6 ; extra == 'docs'
|
|
40
41
|
Requires-Dist: sphinx-rtd-theme <2.1,>=1.3 ; extra == 'docs'
|
|
41
|
-
Requires-Dist: docutils <0.
|
|
42
|
+
Requires-Dist: docutils <0.22 ; extra == 'docs'
|
|
42
43
|
Requires-Dist: toml ; extra == 'docs'
|
|
43
44
|
Requires-Dist: build ; extra == 'docs'
|
|
45
|
+
Requires-Dist: m2r ; extra == 'docs'
|
|
46
|
+
Requires-Dist: typer ; extra == 'docs'
|
|
44
47
|
Provides-Extra: jobs
|
|
45
48
|
Requires-Dist: amazon-braket-sdk >=1.48.1 ; extra == 'jobs'
|
|
46
49
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
qbraid_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
qbraid_cli/_version.py,sha256=
|
|
2
|
+
qbraid_cli/_version.py,sha256=rUwu2eja7AS4Qp4mUrT-oC2PaWQWiRi_YNlcgdK7tAE,424
|
|
3
3
|
qbraid_cli/exceptions.py,sha256=KjlhYJhSHMVazaNiBjD_Ur06w4sekP8zRsFzBdyIpno,672
|
|
4
|
-
qbraid_cli/handlers.py,sha256=
|
|
5
|
-
qbraid_cli/main.py,sha256=
|
|
4
|
+
qbraid_cli/handlers.py,sha256=i3vdRtdy4bZKg3j6fwfVMz1ddhMgzlc2hhmj-vewxpI,6542
|
|
5
|
+
qbraid_cli/main.py,sha256=IRKazFqtFpoiq_xOtqb-IXDKAVs4lacFIRCQCoiGNF4,2503
|
|
6
|
+
qbraid_cli/admin/__init__.py,sha256=Suo_L1_yBodCvLM_fpw8gRIhD4mVVOXKObtxeoMaBVo,150
|
|
7
|
+
qbraid_cli/admin/app.py,sha256=__6lo-iFsbfz-ayD1-AS8X1z_gYhCad1NK17hnrL7HY,1451
|
|
8
|
+
qbraid_cli/admin/headers.py,sha256=oE2Ry9221ZV4tgYqFJNHAPilDw72xR3LqG_gBw7BFxM,6667
|
|
9
|
+
qbraid_cli/admin/validation.py,sha256=LkAVXlHtM0MhCa34MIWrfX59wGXMVlZmdVB4-AQ8fBk,1003
|
|
6
10
|
qbraid_cli/configure/__init__.py,sha256=6GU7vR6JYRGcMsmdrpFbwLO5VSUmnLgwSbtmGWMQND4,158
|
|
7
11
|
qbraid_cli/configure/actions.py,sha256=3rrWHaCAsogyx0Ll-lcjbSzldD4kPuz1z6VQiWebSWw,3203
|
|
8
12
|
qbraid_cli/configure/app.py,sha256=vcPu1Npf8sfGtWGQrjDJQG5vCdeUa3nlpEnZMlVLlWM,1615
|
|
@@ -10,20 +14,20 @@ qbraid_cli/credits/__init__.py,sha256=t-3XAJFAXiu_jI4sgjaIOuNne_AoSYaSEsi-SSRkvP
|
|
|
10
14
|
qbraid_cli/credits/app.py,sha256=iHikmjx8pylMFNzHckuauOg-Nb9pS7xQq_H75ibVJig,774
|
|
11
15
|
qbraid_cli/devices/__init__.py,sha256=_PU3eMQRV4DkPw-oCmfCPh8EbVmgG76ieEKuNsY9Xqc,154
|
|
12
16
|
qbraid_cli/devices/app.py,sha256=zxSxrEQn7irkJoME4S_CBnRqWeB8cqPaBsIMfpdYFk0,2530
|
|
13
|
-
qbraid_cli/devices/validation.py,sha256=
|
|
17
|
+
qbraid_cli/devices/validation.py,sha256=YhShyUufgrKnx2XjXOXF-PqFJYklJT9CgeqIwKcNam4,809
|
|
14
18
|
qbraid_cli/envs/__init__.py,sha256=YgIoMWxfGqzmwfypO5JHYuCOu6BfFwb9NHgQel1IJM8,148
|
|
15
19
|
qbraid_cli/envs/activate.py,sha256=VpvVYSfQDlcmlNWJOgkLIQ2p8YXPPLG8Jbl5t8GHUDw,2140
|
|
16
20
|
qbraid_cli/envs/app.py,sha256=t6bRwJGy-M3PAu870ZsttsM8tpSB0OFasgCJiV9nTSA,8620
|
|
17
21
|
qbraid_cli/envs/create.py,sha256=uCRex_TcFYw26jUOU06Ta5I8Mq5pRqLVaOE6MxrrExs,4337
|
|
18
22
|
qbraid_cli/envs/data_handling.py,sha256=mTVzsj6KleeeYDKGhgD-IesF9KQQMSszKFSEo8Wrv9w,4001
|
|
19
23
|
qbraid_cli/jobs/__init__.py,sha256=bj9XmZ4JL8OtMMZbHIu-DPhpOMXGLSB-W1b0wO7wKro,148
|
|
20
|
-
qbraid_cli/jobs/app.py,sha256=
|
|
21
|
-
qbraid_cli/jobs/toggle_braket.py,sha256=
|
|
22
|
-
qbraid_cli/jobs/validation.py,sha256=
|
|
24
|
+
qbraid_cli/jobs/app.py,sha256=kmg9mYla3Nd7EdjQlFu7IOvm7sejLNfPPA6Qeet-IfE,4898
|
|
25
|
+
qbraid_cli/jobs/toggle_braket.py,sha256=d5C_Di80jWMFlh-77eH8YY9pjMKWXK5abenUDtPlE_I,6662
|
|
26
|
+
qbraid_cli/jobs/validation.py,sha256=xNbjUggMhUs4wzkuRm4PuFPi_wrElYicUgYXLznHz3U,2983
|
|
23
27
|
qbraid_cli/kernels/__init__.py,sha256=VhpBota_v7OoiGxrPCqJU4XBVcolf81mbCYGSxXzVhc,154
|
|
24
28
|
qbraid_cli/kernels/app.py,sha256=ZJWVdKzCDfzGnA1pqp01vDbE7fh8p84jC-y6DDgWlxc,3373
|
|
25
|
-
qbraid_cli-0.8.0.
|
|
26
|
-
qbraid_cli-0.8.0.
|
|
27
|
-
qbraid_cli-0.8.0.
|
|
28
|
-
qbraid_cli-0.8.0.
|
|
29
|
-
qbraid_cli-0.8.0.
|
|
29
|
+
qbraid_cli-0.8.0.dev5.dist-info/METADATA,sha256=emAQ5AjqtbZ9_l0lSBUtU4Jizzrru3GPPsxnUfriZds,5919
|
|
30
|
+
qbraid_cli-0.8.0.dev5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
31
|
+
qbraid_cli-0.8.0.dev5.dist-info/entry_points.txt,sha256=c5ZJ7NjbxhDqMpou9q5F03_b_KG34HzFDijIDmEIwgQ,47
|
|
32
|
+
qbraid_cli-0.8.0.dev5.dist-info/top_level.txt,sha256=LTYJgeYSCHo9Il8vZu0yIPuGdGyNaIw6iRy6BeoZo8o,11
|
|
33
|
+
qbraid_cli-0.8.0.dev5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|