qbraid-cli 0.7.1__py3-none-any.whl → 0.8.0__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.

Files changed (41) hide show
  1. qbraid_cli/_version.py +14 -6
  2. qbraid_cli/admin/__init__.py +9 -0
  3. qbraid_cli/admin/app.py +50 -0
  4. qbraid_cli/admin/headers.py +193 -0
  5. qbraid_cli/admin/validation.py +33 -0
  6. qbraid_cli/configure/__init__.py +9 -0
  7. qbraid_cli/configure/actions.py +111 -0
  8. qbraid_cli/configure/app.py +77 -0
  9. qbraid_cli/credits/__init__.py +9 -0
  10. qbraid_cli/credits/app.py +32 -0
  11. qbraid_cli/devices/__init__.py +9 -0
  12. qbraid_cli/devices/app.py +80 -0
  13. qbraid_cli/devices/validation.py +26 -0
  14. qbraid_cli/envs/__init__.py +9 -0
  15. qbraid_cli/envs/activate.py +65 -0
  16. qbraid_cli/envs/app.py +270 -0
  17. qbraid_cli/envs/create.py +128 -0
  18. qbraid_cli/envs/data_handling.py +140 -0
  19. qbraid_cli/exceptions.py +17 -5
  20. qbraid_cli/handlers.py +168 -0
  21. qbraid_cli/jobs/__init__.py +9 -0
  22. qbraid_cli/jobs/app.py +149 -0
  23. qbraid_cli/jobs/toggle_braket.py +185 -0
  24. qbraid_cli/jobs/validation.py +93 -0
  25. qbraid_cli/kernels/__init__.py +9 -0
  26. qbraid_cli/kernels/app.py +111 -0
  27. qbraid_cli/main.py +80 -0
  28. {qbraid_cli-0.7.1.dist-info → qbraid_cli-0.8.0.dist-info}/METADATA +68 -46
  29. qbraid_cli-0.8.0.dist-info/RECORD +33 -0
  30. {qbraid_cli-0.7.1.dist-info → qbraid_cli-0.8.0.dist-info}/WHEEL +1 -1
  31. qbraid_cli-0.8.0.dist-info/entry_points.txt +2 -0
  32. qbraid_cli/_display.py +0 -44
  33. qbraid_cli/bin/qbraid.sh +0 -1346
  34. qbraid_cli/configure.py +0 -113
  35. qbraid_cli/envs.py +0 -195
  36. qbraid_cli/jobs.py +0 -226
  37. qbraid_cli/wrapper.py +0 -103
  38. qbraid_cli-0.7.1.data/scripts/qbraid.sh +0 -1346
  39. qbraid_cli-0.7.1.dist-info/RECORD +0 -15
  40. qbraid_cli-0.7.1.dist-info/entry_points.txt +0 -2
  41. {qbraid_cli-0.7.1.dist-info → qbraid_cli-0.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module supporting 'qbraid envs activate' command.
6
+
7
+ """
8
+
9
+ import os
10
+ from pathlib import Path
11
+
12
+ import typer
13
+
14
+
15
+ def find_shell_rc(shell_path: str) -> str:
16
+ """Finds an existing shell configuration file in the user's home directory."""
17
+ if "bash" not in shell_path:
18
+ raise ValueError(f"Unsupported shell: {shell_path}")
19
+
20
+ possible_files = [".bashrc", ".bash_profile", ".bash_login"]
21
+
22
+ for file_name in possible_files:
23
+ rc_file = Path.home() / file_name
24
+ if rc_file.exists():
25
+ return str(rc_file)
26
+
27
+ raise FileNotFoundError(f"No {shell_path} configuration file found in the home directory.")
28
+
29
+
30
+ def print_activate_command(venv_path: Path) -> None:
31
+ """Prints the command to activate the virtual environment with improved formatting."""
32
+ typer.echo("To activate this environment, use command:\n")
33
+ if os.name == "nt":
34
+ # Windows operating system
35
+ activate_script = venv_path / "Scripts" / "activate"
36
+ activate_script_ps = venv_path / "Scripts" / "Activate.ps1"
37
+ typer.echo("\t$ " + str(activate_script))
38
+ typer.echo("\nOr for PowerShell, use:\n")
39
+ typer.echo("\t$ " + f"& {activate_script_ps}")
40
+ else:
41
+ # Unix-like operating systems (Linux/macOS)
42
+ activate_script = venv_path / "bin" / "activate"
43
+ typer.echo("\t$ " + f"source {activate_script}")
44
+ typer.echo("")
45
+ raise typer.Exit()
46
+
47
+
48
+ def activate_pyvenv(venv_path: Path):
49
+ """Activate the virtual environment."""
50
+ shell_path = os.environ.get("SHELL")
51
+
52
+ if shell_path is None:
53
+ print_activate_command(venv_path)
54
+ return # Return early since we can't proceed without a shell
55
+ try:
56
+ shell_rc = find_shell_rc(shell_path)
57
+ except (FileNotFoundError, ValueError):
58
+ print_activate_command(venv_path)
59
+ return # Return early since no suitable shell rc file was found
60
+ bin_path = str(venv_path / "bin")
61
+
62
+ os.system(
63
+ f"cat {shell_rc} {bin_path}/activate > {bin_path}/activate2 && "
64
+ f"{shell_path} --rcfile {bin_path}/activate2"
65
+ )
qbraid_cli/envs/app.py ADDED
@@ -0,0 +1,270 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module defining commands in the 'qbraid envs' namespace.
6
+
7
+ """
8
+
9
+ import shutil
10
+ import subprocess
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Any, Dict, Optional, Tuple
14
+
15
+ import typer
16
+ from rich.console import Console
17
+
18
+ from qbraid_cli.envs.data_handling import installed_envs_data, request_delete_env, validate_env_name
19
+ from qbraid_cli.handlers import QbraidException, run_progress_task
20
+
21
+ envs_app = typer.Typer(help="Manage qBraid environments.")
22
+
23
+
24
+ @envs_app.command(name="create")
25
+ def envs_create( # pylint: disable=too-many-statements
26
+ name: str = typer.Option(
27
+ ..., "--name", "-n", help="Name of the environment to create", callback=validate_env_name
28
+ ),
29
+ description: Optional[str] = typer.Option(
30
+ None, "--description", "-d", help="Short description of the environment"
31
+ ),
32
+ auto_confirm: bool = typer.Option(
33
+ False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
34
+ ),
35
+ ) -> None:
36
+ """Create a new qBraid environment."""
37
+ from .create import create_qbraid_env_assets, create_venv
38
+
39
+ def request_new_env(req_body: Dict[str, str]) -> Dict[str, Any]:
40
+ """Send request to create new environment and return the slug."""
41
+ from qbraid_core import QbraidSession, RequestsApiError
42
+
43
+ session = QbraidSession()
44
+
45
+ try:
46
+ env_data = session.post("/environments/create", json=req_body).json()
47
+ except RequestsApiError as err:
48
+ raise QbraidException("Create environment request failed") from err
49
+
50
+ if env_data is None or len(env_data) == 0 or env_data.get("slug") is None:
51
+ raise QbraidException(
52
+ "Create environment request responsed with invalid environment data"
53
+ )
54
+
55
+ return env_data
56
+
57
+ def gather_local_data() -> Tuple[Path, str]:
58
+ """Gather environment data and return the slug."""
59
+ from qbraid_core.services.environments import get_default_envs_paths
60
+
61
+ env_path = get_default_envs_paths()[0]
62
+
63
+ result = subprocess.run(
64
+ [sys.executable, "--version"],
65
+ capture_output=True,
66
+ text=True,
67
+ check=True,
68
+ )
69
+
70
+ python_version = result.stdout or result.stderr
71
+
72
+ return env_path, python_version
73
+
74
+ req_body = {
75
+ "name": name,
76
+ "description": description or "",
77
+ "tags": "", # comma separated list of tags
78
+ "code": "", # newline separated list of packages
79
+ "visibility": "private",
80
+ "kernelName": "",
81
+ "prompt": "",
82
+ "origin": "CLI",
83
+ }
84
+
85
+ environment = run_progress_task(
86
+ request_new_env,
87
+ req_body,
88
+ description="Validating request...",
89
+ error_message="Failed to create qBraid environment",
90
+ )
91
+
92
+ env_path, python_version = run_progress_task(
93
+ gather_local_data,
94
+ description="Solving environment...",
95
+ error_message="Failed to create qBraid environment",
96
+ )
97
+
98
+ slug = environment.get("slug")
99
+ display_name = environment.get("displayName")
100
+ prompt = environment.get("prompt")
101
+ description = environment.get("description")
102
+ tags = environment.get("tags")
103
+ kernel_name = environment.get("kernelName")
104
+
105
+ slug_path = env_path / slug
106
+ description = "None" if description == "" else description
107
+
108
+ typer.echo("\n\n## qBraid Metadata ##\n")
109
+ typer.echo(f" name: {display_name}")
110
+ typer.echo(f" description: {description}")
111
+ typer.echo(f" tags: {tags}")
112
+ typer.echo(f" slug: {slug}")
113
+ typer.echo(f" shellPrompt: {prompt}")
114
+ typer.echo(f" kernelName: {kernel_name}")
115
+
116
+ typer.echo("\n\n## Environment Plan ##\n")
117
+ typer.echo(f" location: {slug_path}")
118
+ typer.echo(f" version: {python_version}\n")
119
+
120
+ user_confirmation = auto_confirm or typer.confirm("Proceed", default=True)
121
+ typer.echo("")
122
+ if not user_confirmation:
123
+ request_delete_env(slug)
124
+ typer.echo("qBraidSystemExit: Exiting.")
125
+ raise typer.Exit()
126
+
127
+ run_progress_task(
128
+ create_qbraid_env_assets,
129
+ slug,
130
+ prompt,
131
+ kernel_name,
132
+ slug_path,
133
+ description="Generating qBraid assets...",
134
+ error_message="Failed to create qBraid environment",
135
+ )
136
+
137
+ run_progress_task(
138
+ create_venv,
139
+ slug_path,
140
+ prompt,
141
+ description="Creating virtual environment...",
142
+ error_message="Failed to create qBraid environment",
143
+ )
144
+
145
+ console = Console()
146
+ console.print(
147
+ f"\n[bold green]Successfully created qBraid environment: "
148
+ f"[/bold green][bold magenta]{name}[/bold magenta]\n"
149
+ )
150
+ typer.echo("# To activate this environment, use")
151
+ typer.echo("#")
152
+ typer.echo(f"# $ qbraid envs activate {name}")
153
+ typer.echo("#")
154
+ typer.echo("# To deactivate an active environment, use")
155
+ typer.echo("#")
156
+ typer.echo("# $ deactivate")
157
+
158
+
159
+ @envs_app.command(name="remove")
160
+ def envs_remove(
161
+ name: str = typer.Option(..., "-n", "--name", help="Name of the environment to remove"),
162
+ auto_confirm: bool = typer.Option(
163
+ False, "--yes", "-y", help="Automatically answer 'yes' to all prompts"
164
+ ),
165
+ ) -> None:
166
+ """Delete a qBraid environment."""
167
+
168
+ def gather_local_data(env_name: str) -> Tuple[Path, str]:
169
+ """Get environment path and slug from name (alias)."""
170
+ installed, aliases = installed_envs_data()
171
+ for alias, slug in aliases.items():
172
+ if alias == env_name:
173
+ path = installed[slug]
174
+
175
+ return path, slug
176
+
177
+ raise QbraidException(f"Environment '{name}' not found.")
178
+
179
+ slug_path, slug = gather_local_data(name)
180
+
181
+ confirmation_message = (
182
+ f"⚠️ Warning: You are about to delete the environment '{name}' "
183
+ f"located at '{slug_path}'.\n"
184
+ "This will remove all local packages and permanently delete all "
185
+ "of its associated qBraid environment metadata.\n"
186
+ "This operation CANNOT be undone.\n\n"
187
+ "Are you sure you want to continue?"
188
+ )
189
+
190
+ if auto_confirm or typer.confirm(confirmation_message, abort=True):
191
+ typer.echo("")
192
+ run_progress_task(
193
+ request_delete_env,
194
+ slug,
195
+ description="Deleting remote environment data...",
196
+ error_message="Failed to delete qBraid environment",
197
+ )
198
+
199
+ run_progress_task(
200
+ shutil.rmtree,
201
+ slug_path,
202
+ description="Deleting local environment...",
203
+ error_message="Failed to delete qBraid environment",
204
+ )
205
+ typer.echo(f"\nEnvironment '{name}' successfully removed.")
206
+
207
+
208
+ @envs_app.command(name="list")
209
+ def envs_list():
210
+ """List installed qBraid environments."""
211
+ installed, aliases = installed_envs_data()
212
+
213
+ console = Console()
214
+
215
+ if len(installed) == 0:
216
+ console.print(
217
+ "No qBraid environments installed.\n\n"
218
+ + "Use 'qbraid envs create' to create a new environment.",
219
+ style="yellow",
220
+ )
221
+ return
222
+
223
+ alias_path_pairs = [(alias, installed[slug_name]) for alias, slug_name in aliases.items()]
224
+ sorted_alias_path_pairs = sorted(
225
+ alias_path_pairs,
226
+ key=lambda x: (x[0] != "default", str(x[1]).startswith(str(Path.home())), x[0]),
227
+ )
228
+
229
+ current_env_path = Path(sys.executable).parent.parent.parent
230
+
231
+ max_alias_length = (
232
+ max(len(str(alias)) for alias, envpath in sorted_alias_path_pairs)
233
+ if sorted_alias_path_pairs
234
+ else 0
235
+ )
236
+
237
+ print("# qbraid environments:")
238
+ print("#")
239
+ print("")
240
+
241
+ for alias, path in sorted_alias_path_pairs:
242
+ mark = "*" if path == current_env_path else " "
243
+ # Format each line with spacing based on the longest alias for alignment
244
+ line = f"{alias.ljust(max_alias_length+3)}{mark} {path}" # fix the most optimal spacing
245
+ console.print(line)
246
+
247
+
248
+ @envs_app.command(name="activate")
249
+ def envs_activate(
250
+ name: str = typer.Argument(..., help="Name of the environment. Values from 'qbraid envs list'.")
251
+ ):
252
+ """Activate qBraid environment.
253
+
254
+ NOTE: Currently only works on qBraid Lab platform, and select few other OS types.
255
+ """
256
+ installed, aliases = installed_envs_data()
257
+ if name in aliases:
258
+ venv_path: Path = installed[aliases[name]] / "pyenv"
259
+ elif name in installed:
260
+ venv_path: Path = installed[name] / "pyenv"
261
+ else:
262
+ raise typer.BadParameter(f"Environment '{name}' not found.")
263
+
264
+ from .activate import activate_pyvenv
265
+
266
+ activate_pyvenv(venv_path)
267
+
268
+
269
+ if __name__ == "__main__":
270
+ envs_app()
@@ -0,0 +1,128 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module supporting 'qbraid envs create' command.
6
+
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ from typing import Optional
15
+
16
+
17
+ def replace_str(target: str, replacement: str, file_path: str) -> None:
18
+ """Replace all instances of string in file"""
19
+ with open(file_path, "r", encoding="utf-8") as file:
20
+ content = file.read()
21
+
22
+ content = content.replace(target, replacement)
23
+
24
+ with open(file_path, "w", encoding="utf-8") as file:
25
+ file.write(content)
26
+
27
+
28
+ def update_state_json(
29
+ slug_path: str,
30
+ install_complete: int,
31
+ install_success: int,
32
+ message: Optional[str] = None,
33
+ env_name: Optional[str] = None,
34
+ ) -> None:
35
+ """Update environment's install status values in a JSON file.
36
+ Truth table values: 0 = False, 1 = True, -1 = Unknown
37
+ """
38
+ # Set default message if none provided
39
+ message = "" if message is None else message.replace("\n", " ")
40
+
41
+ # File path for state.json
42
+ state_json_path = os.path.join(slug_path, "state.json")
43
+
44
+ # Read existing data or use default structure
45
+ if os.path.exists(state_json_path):
46
+ with open(state_json_path, "r", encoding="utf-8") as f:
47
+ data = json.load(f)
48
+ else:
49
+ data = {"install": {}}
50
+
51
+ # Update the data
52
+ data["install"]["complete"] = install_complete
53
+ data["install"]["success"] = install_success
54
+ data["install"]["message"] = message
55
+
56
+ if env_name is not None:
57
+ data["name"] = env_name
58
+
59
+ # Write updated data back to state.json
60
+ with open(state_json_path, "w", encoding="utf-8") as f:
61
+ json.dump(data, f, indent=4)
62
+
63
+
64
+ def create_venv(slug_path: str, prompt: str) -> None:
65
+ """Create virtual environment and swap PS1 display name."""
66
+ venv_path = os.path.join(slug_path, "pyenv")
67
+ subprocess.run([sys.executable, "-m", "venv", venv_path], check=True)
68
+
69
+ # Determine the correct directory for activation scripts based on the operating system
70
+ if sys.platform == "win32":
71
+ scripts_path = os.path.join(venv_path, "Scripts")
72
+ activate_files = ["activate.bat", "Activate.ps1"]
73
+ else:
74
+ scripts_path = os.path.join(venv_path, "bin")
75
+ activate_files = ["activate", "activate.csh", "activate.fish"]
76
+
77
+ for file in activate_files:
78
+ file_path = os.path.join(scripts_path, file)
79
+ if os.path.isfile(file_path):
80
+ replace_str("(pyenv)", f"({prompt})", file_path)
81
+
82
+ replace_str(
83
+ "include-system-site-packages = false",
84
+ "include-system-site-packages = true",
85
+ os.path.join(venv_path, "pyvenv.cfg"),
86
+ )
87
+
88
+ update_state_json(slug_path, 1, 1)
89
+
90
+
91
+ def create_qbraid_env_assets(slug: str, alias: str, kernel_name: str, slug_path: str) -> None:
92
+ """Create a qBraid environment including python venv, PS1 configs,
93
+ kernel resource files, and qBraid state.json."""
94
+ # pylint: disable-next=import-outside-toplevel
95
+ from jupyter_client.kernelspec import KernelSpecManager
96
+
97
+ local_resource_dir = os.path.join(slug_path, "kernels", f"python3_{slug}")
98
+ os.makedirs(local_resource_dir, exist_ok=True)
99
+
100
+ # create state.json
101
+ update_state_json(slug_path, 0, 0, env_name=alias)
102
+
103
+ # create kernel.json
104
+ kernel_json_path = os.path.join(local_resource_dir, "kernel.json")
105
+ kernel_spec_manager = KernelSpecManager()
106
+ kernelspec_dict = kernel_spec_manager.get_all_specs()
107
+ kernel_data = kernelspec_dict["python3"]["spec"]
108
+ if sys.platform == "win32":
109
+ python_exec_path = os.path.join(slug_path, "pyenv", "Scripts", "python.exe")
110
+ else:
111
+ python_exec_path = os.path.join(slug_path, "pyenv", "bin", "python")
112
+ kernel_data["argv"][0] = python_exec_path
113
+ kernel_data["display_name"] = kernel_name
114
+ with open(kernel_json_path, "w", encoding="utf-8") as file:
115
+ json.dump(kernel_data, file, indent=4)
116
+
117
+ # copy logo files
118
+ sys_resource_dir = kernelspec_dict["python3"]["resource_dir"]
119
+ logo_files = ["logo-32x32.png", "logo-64x64.png", "logo-svg.svg"]
120
+
121
+ for file in logo_files:
122
+ sys_path = os.path.join(sys_resource_dir, file)
123
+ loc_path = os.path.join(local_resource_dir, file)
124
+ if os.path.isfile(sys_path):
125
+ shutil.copy(sys_path, loc_path)
126
+
127
+ # create python venv
128
+ create_venv(slug_path, alias)
@@ -0,0 +1,140 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
4
+ """
5
+ Module for handling data related to qBraid environments.
6
+
7
+ """
8
+
9
+ import json
10
+ import keyword
11
+ import re
12
+ from pathlib import Path
13
+ from typing import Dict, List, Tuple
14
+
15
+ import typer
16
+
17
+ from qbraid_cli.handlers import QbraidException
18
+
19
+
20
+ def is_valid_env_name(env_name: str) -> bool: # pylint: disable=too-many-return-statements
21
+ """
22
+ Validates a Python virtual environment name against best practices.
23
+
24
+ This function checks if the given environment name is valid based on certain
25
+ criteria, including length, use of special characters, reserved names, and
26
+ operating system-specific restrictions.
27
+
28
+ Args:
29
+ env_name (str): The name of the Python virtual environment to validate.
30
+
31
+ Returns:
32
+ bool: True if the name is valid, False otherwise.
33
+
34
+ Raises:
35
+ ValueError: If the environment name is not a string or is empty.
36
+ """
37
+ # Basic checks for empty names or purely whitespace names
38
+ if not env_name or env_name.isspace():
39
+ return False
40
+
41
+ # Check for invalid characters, including shell metacharacters and spaces
42
+ if re.search(r'[<>:"/\\|?*\s&;()$[\]#~!{}]', env_name):
43
+ return False
44
+
45
+ if env_name.startswith("tmp"):
46
+ return False
47
+
48
+ # Reserved names for Windows (example list, can be expanded)
49
+ reserved_names = [
50
+ "CON",
51
+ "PRN",
52
+ "AUX",
53
+ "NUL",
54
+ "COM1",
55
+ "COM2",
56
+ "COM3",
57
+ "COM4",
58
+ "COM5",
59
+ "COM6",
60
+ "COM7",
61
+ "COM8",
62
+ "COM9",
63
+ "LPT1",
64
+ "LPT2",
65
+ "LPT3",
66
+ "LPT4",
67
+ "LPT5",
68
+ "LPT6",
69
+ "LPT7",
70
+ "LPT8",
71
+ "LPT9",
72
+ ]
73
+ if env_name.upper() in reserved_names:
74
+ return False
75
+
76
+ if len(env_name) > 20:
77
+ return False
78
+
79
+ # Check against Python reserved words
80
+ if keyword.iskeyword(env_name):
81
+ return False
82
+
83
+ # Check if it starts with a number, which is not a good practice
84
+ if env_name[0].isdigit():
85
+ return False
86
+
87
+ return True
88
+
89
+
90
+ def validate_env_name(value: str) -> str:
91
+ """Validate environment name."""
92
+ if not is_valid_env_name(value):
93
+ raise typer.BadParameter(
94
+ f"Invalid environment name '{value}'. " "Please use a valid Python environment name."
95
+ )
96
+ return value
97
+
98
+
99
+ def installed_envs_data() -> Tuple[Dict[str, Path], Dict[str, str]]:
100
+ """Gather paths and aliases for all installed qBraid environments."""
101
+ from qbraid_core.services.environments.paths import get_default_envs_paths, is_valid_slug
102
+
103
+ installed = {}
104
+ aliases = {}
105
+
106
+ qbraid_env_paths: List[Path] = get_default_envs_paths()
107
+
108
+ for env_path in qbraid_env_paths:
109
+ for entry in env_path.iterdir():
110
+ if entry.is_dir() and is_valid_slug(entry.name):
111
+ installed[entry.name] = entry
112
+
113
+ if entry.name == "qbraid_000000":
114
+ aliases["default"] = entry.name
115
+ continue
116
+
117
+ state_json_path = entry / "state.json"
118
+ if state_json_path.exists():
119
+ try:
120
+ with open(state_json_path, "r", encoding="utf-8") as f:
121
+ data = json.load(f)
122
+ aliases[data.get("name", entry.name[:-7])] = entry.name
123
+ # pylint: disable-next=broad-exception-caught
124
+ except (json.JSONDecodeError, Exception):
125
+ aliases[entry.name[:-7]] = entry.name
126
+ else:
127
+ aliases[entry.name[:-7]] = entry.name
128
+ return installed, aliases
129
+
130
+
131
+ def request_delete_env(slug: str) -> str:
132
+ """Send request to delete environment given slug."""
133
+ from qbraid_core import QbraidSession, RequestsApiError
134
+
135
+ session = QbraidSession()
136
+
137
+ try:
138
+ session.delete(f"/environments/{slug}")
139
+ except RequestsApiError as err:
140
+ raise QbraidException("Delete environment request failed") from err
qbraid_cli/exceptions.py CHANGED
@@ -1,12 +1,24 @@
1
+ # Copyright (c) 2024, qBraid Development Team
2
+ # All rights reserved.
3
+
1
4
  """
2
- Module defining exceptions raised by the qBraid CLI.
5
+ Module defining custom exceptions for the qBraid CLI.
3
6
 
4
7
  """
5
8
 
9
+ from typing import Optional
10
+
11
+ DEFAULT_ERROR_MESSAGE = (
12
+ "An unexpected error occurred while processing your qBraid CLI command. "
13
+ "Please check your input and try again. If the problem persists, "
14
+ "visit https://github.com/qBraid/qBraid-Lab/issues to file a bug report."
15
+ )
6
16
 
7
- class QbraidCLIException(Exception):
8
- """Base class for exceptions raised by the qBraid CLI."""
9
17
 
18
+ class QbraidException(Exception):
19
+ """Custom exception class for qBraid CLI errors."""
10
20
 
11
- class QuantumJobsException(QbraidCLIException):
12
- """Class for exceptions raised by qBraid Quantum Jobs."""
21
+ def __init__(self, message: Optional[str] = None):
22
+ if message is None:
23
+ message = DEFAULT_ERROR_MESSAGE
24
+ super().__init__(message)