holoscan-cli 2.9.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.
Files changed (39) hide show
  1. holoscan_cli/__init__.py +35 -0
  2. holoscan_cli/__main__.py +164 -0
  3. holoscan_cli/common/argparse_types.py +156 -0
  4. holoscan_cli/common/artifact_sources.py +160 -0
  5. holoscan_cli/common/constants.py +119 -0
  6. holoscan_cli/common/dockerutils.py +521 -0
  7. holoscan_cli/common/enum_types.py +49 -0
  8. holoscan_cli/common/exceptions.py +126 -0
  9. holoscan_cli/common/sdk_utils.py +195 -0
  10. holoscan_cli/common/utils.py +137 -0
  11. holoscan_cli/logging.json +37 -0
  12. holoscan_cli/nics/__init__.py +15 -0
  13. holoscan_cli/nics/nics.py +33 -0
  14. holoscan_cli/package-source.json +32 -0
  15. holoscan_cli/packager/__init__.py +15 -0
  16. holoscan_cli/packager/arguments.py +148 -0
  17. holoscan_cli/packager/config_reader.py +180 -0
  18. holoscan_cli/packager/container_builder.py +426 -0
  19. holoscan_cli/packager/manifest_files.py +217 -0
  20. holoscan_cli/packager/models.py +90 -0
  21. holoscan_cli/packager/package_command.py +197 -0
  22. holoscan_cli/packager/packager.py +124 -0
  23. holoscan_cli/packager/parameters.py +603 -0
  24. holoscan_cli/packager/platforms.py +426 -0
  25. holoscan_cli/packager/templates/Dockerfile.jinja2 +479 -0
  26. holoscan_cli/packager/templates/dockerignore +92 -0
  27. holoscan_cli/packager/templates/tools.sh +414 -0
  28. holoscan_cli/py.typed +14 -0
  29. holoscan_cli/runner/__init__.py +15 -0
  30. holoscan_cli/runner/resources.py +185 -0
  31. holoscan_cli/runner/run_command.py +207 -0
  32. holoscan_cli/runner/runner.py +340 -0
  33. holoscan_cli/version/__init__.py +15 -0
  34. holoscan_cli/version/version.py +53 -0
  35. holoscan_cli-2.9.0.dist-info/LICENSE +201 -0
  36. holoscan_cli-2.9.0.dist-info/METADATA +102 -0
  37. holoscan_cli-2.9.0.dist-info/RECORD +39 -0
  38. holoscan_cli-2.9.0.dist-info/WHEEL +4 -0
  39. holoscan_cli-2.9.0.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,521 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ import json
16
+ import logging
17
+ import os
18
+ import posixpath
19
+ import re
20
+ import subprocess
21
+ from pathlib import Path
22
+ from typing import Optional
23
+
24
+ from python_on_whales import docker
25
+
26
+ from ..common.utils import run_cmd_output
27
+ from .constants import DefaultValues, EnvironmentVariables
28
+ from .enum_types import PlatformConfiguration, SdkType
29
+ from .exceptions import GpuResourceError, InvalidManifestError, RunContainerError
30
+ from .utils import get_gpu_count, get_requested_gpus
31
+
32
+ logger = logging.getLogger("common")
33
+
34
+
35
+ def parse_docker_image_name_and_tag(
36
+ image_name: str,
37
+ ) -> tuple[Optional[str], Optional[str]]:
38
+ """Parse a given Docker image name and tag.
39
+
40
+ Args:
41
+ image_name (str): Docker image name and optionally a tag
42
+
43
+ Returns:
44
+ Tuple[Optional[str], Optional[str]]: a tuple with first item as the name of the image
45
+ and tag as the second item
46
+ """
47
+ match = re.search(
48
+ r"^(?P<name>([\w.\-_]+((:\d+|)(?=/[a-z0-9._-]+/[a-z0-9._-]+))|)(/?)([a-z0-9.\-_/]+(/[a-z0-9.\-_]+|)))(:(?P<tag>[\w.\-_]{1,127})|)$",
49
+ image_name,
50
+ )
51
+
52
+ if match is None or match.group("name") is None:
53
+ return None, None
54
+
55
+ name = match.group("name")
56
+ tag = match.group("tag") if match.group("tag") else None
57
+
58
+ return (name, tag)
59
+
60
+
61
+ def create_or_use_network(network: Optional[str], image_name: Optional[str]) -> str:
62
+ """Create a Docker network by the given name if not already exists.
63
+
64
+ Args:
65
+ network (Optional[str]): name of the network to create
66
+ image_name (Optional[str]): name of the image used to generate a network name from
67
+
68
+ Raises:
69
+ RunContainerError: when unable to retrieve the specified network or failed to create one.
70
+
71
+ Returns:
72
+ str: network name
73
+ """
74
+ if network is None and image_name is not None:
75
+ network = image_name.split(":")[0]
76
+ network += "-network"
77
+
78
+ assert network is not None
79
+
80
+ try:
81
+ networks = docker.network.list(filters={"name": f"^{network}$"})
82
+ if len(networks) > 0:
83
+ return networks[0].name
84
+ except Exception as ex:
85
+ raise RunContainerError(f"error retrieving network information: {ex}") from ex
86
+
87
+ try:
88
+ return docker.network.create(network, driver="bridge").name
89
+ except Exception as ex:
90
+ raise RunContainerError(f"error creating Docker network: {ex}") from ex
91
+
92
+
93
+ def image_exists(image_name: str) -> bool:
94
+ """Checks if the Docker image exists.
95
+
96
+ Args:
97
+ image_name (str): name of the Docker image
98
+
99
+ Returns:
100
+ bool: whether the image exists or not.
101
+ """
102
+ if image_name is None:
103
+ return False
104
+ try:
105
+ if not docker.image.exists(image_name):
106
+ logger.info(f"Attempting to pull image {image_name}..")
107
+ docker.image.pull(image_name)
108
+ return docker.image.exists(image_name)
109
+ except Exception as e:
110
+ logger.error(str(e))
111
+ return False
112
+
113
+
114
+ def docker_export_tarball(file: str, tag: str):
115
+ """Exports the docker image to a file
116
+
117
+ Args:
118
+ file (str): name of the exported file
119
+ tag (str): source Docker image tag
120
+ """
121
+ docker.image.save(tag, file)
122
+
123
+
124
+ def create_and_get_builder(builder_name: str):
125
+ """Creates a Docker BuildX builder
126
+
127
+ Args:
128
+ builder_name (str): name of the builder to create
129
+
130
+ Returns:
131
+ _type_: name of the builder created
132
+ """
133
+ builders = docker.buildx.list()
134
+ for builder in builders:
135
+ if builder.name == builder_name:
136
+ logger.info(f"Using existing Docker BuildKit builder `{builder_name}`")
137
+ return builder_name
138
+
139
+ logger.info(
140
+ f"Creating Docker BuildKit builder `{builder_name}` using `docker-container`"
141
+ )
142
+ builder = docker.buildx.create(
143
+ name=builder_name, driver="docker-container", driver_options={"network": "host"}
144
+ )
145
+ return builder.name
146
+
147
+
148
+ def build_docker_image(**kwargs):
149
+ """Builds a Docker image"""
150
+ _ = docker.buildx.build(**kwargs)
151
+
152
+
153
+ def docker_run(
154
+ name: str,
155
+ image_name: str,
156
+ input_path: Optional[Path],
157
+ output_path: Optional[Path],
158
+ app_info: dict,
159
+ pkg_info: dict,
160
+ quiet: bool,
161
+ commands: list[str],
162
+ health_check: bool,
163
+ network: str,
164
+ network_interface: Optional[str],
165
+ use_all_nics: bool,
166
+ gpu_enum: Optional[str],
167
+ config: Optional[Path],
168
+ render: bool,
169
+ user: str,
170
+ terminal: bool,
171
+ devices: list[str],
172
+ platform_config: str,
173
+ shared_memory_size: str = "1GB",
174
+ is_root: bool = False,
175
+ ):
176
+ """Creates and runs a Docker container
177
+
178
+ `HOLOSCAN_HOSTING_SERVICE` environment variable is used for hiding the help message
179
+ inside the tools.sh when the users run the container using holoscan run.
180
+
181
+ Args:
182
+ image_name (str): Docker image name
183
+ input_path (Optional[Path]): input data path
184
+ output_path (Optional[Path]): output data path
185
+ app_info (dict): app manifest
186
+ pkg_info (dict): package manifest
187
+ quiet (bool): prints only stderr when True, otherwise, prints all logs
188
+ commands (List[str]): list of arguments to provide to the container
189
+ health_check (bool): whether or not to enable the gRPC health check service
190
+ network (str): Docker network to associate the container with
191
+ network_interface (Optional[str]): Name of the network interface for setting
192
+ UCX_NET_DEVICES
193
+ use_all_nics (bool): Sets UCX_CM_USE_ALL_DEVICES to 'y' if True
194
+ config (Optional[Path]): optional configuration file for overriding the embedded one
195
+ render (bool): whether or not to enable graphic rendering
196
+ user (str): UID and GID to associate with the container
197
+ terminal (bool): whether or not to enter bash terminal
198
+ devices (List[str]): list of devices to be mapped into the container
199
+ platformConfig (str): platform configuration value used when packaging the application,
200
+ shared_memory_size (str): size of /dev/shm,
201
+ is_root (bool): whether the user is root (UID = 0) or not
202
+ """
203
+
204
+ volumes = []
205
+ environment_variables = {
206
+ "NVIDIA_DRIVER_CAPABILITIES": "all",
207
+ "HOLOSCAN_HOSTING_SERVICE": "HOLOSCAN_RUN",
208
+ "UCX_CM_USE_ALL_DEVICES": "y" if use_all_nics else "n",
209
+ }
210
+
211
+ if network_interface is not None:
212
+ environment_variables["UCX_NET_DEVICES"] = network_interface
213
+
214
+ if health_check:
215
+ environment_variables["HOLOSCAN_ENABLE_HEALTH_CHECK"] = "true"
216
+
217
+ if logger.root.level == logging.DEBUG:
218
+ environment_variables["UCX_LOG_LEVEL"] = "DEBUG"
219
+ environment_variables["VK_LOADER_DEBUG"] = "all"
220
+
221
+ if render:
222
+ volumes.append(("/tmp/.X11-unix", "/tmp/.X11-unix"))
223
+ display = os.environ.get("DISPLAY", None)
224
+ if display is not None:
225
+ environment_variables["DISPLAY"] = display
226
+ xdg_session_type = os.environ.get("XDG_SESSION_TYPE", None)
227
+ if xdg_session_type is not None:
228
+ environment_variables["XDG_SESSION_TYPE"] = xdg_session_type
229
+ xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR", None)
230
+ if xdg_runtime_dir is not None:
231
+ volumes.append((xdg_runtime_dir, xdg_runtime_dir))
232
+ environment_variables["XDG_RUNTIME_DIR"] = xdg_runtime_dir
233
+ wayland_display = os.environ.get("WAYLAND_DISPLAY", None)
234
+ if wayland_display is not None:
235
+ environment_variables["WAYLAND_DISPLAY"] = wayland_display
236
+
237
+ # Use user-specified --gpu values
238
+ if gpu_enum is not None:
239
+ environment_variables["NVIDIA_VISIBLE_DEVICES"] = gpu_enum
240
+ # If the image was built for iGPU but the system is configured for dGPU, attempt
241
+ # targeting the system's iGPU using the CDI spec
242
+ elif (
243
+ platform_config == PlatformConfiguration.iGPU.value
244
+ and not _host_is_native_igpu()
245
+ ):
246
+ environment_variables["NVIDIA_VISIBLE_DEVICES"] = "nvidia.com/igpu=0"
247
+ logger.info(
248
+ "Attempting to run an image for iGPU (integrated GPU) on a system configured "
249
+ "with a dGPU (discrete GPU). If this is correct (ex: IGX Orin developer kit), "
250
+ "make sure to enable iGPU on dGPU support as described in your developer kit "
251
+ "user guide. If not, either rebuild the image for dGPU or run this image on a "
252
+ "system configured for iGPU only (ex: Jetson AGX, Nano...)."
253
+ )
254
+ # Otherwise, read specs from package manifest
255
+ else:
256
+ requested_gpus = get_requested_gpus(pkg_info)
257
+ available_gpus = get_gpu_count()
258
+
259
+ if available_gpus < requested_gpus:
260
+ raise GpuResourceError(
261
+ f"Available GPUs ({available_gpus}) are less than required ({requested_gpus}). "
262
+ )
263
+
264
+ if requested_gpus == 0:
265
+ environment_variables["NVIDIA_VISIBLE_DEVICES"] = "all"
266
+ else:
267
+ environment_variables["NVIDIA_VISIBLE_DEVICES"] = ",".join(
268
+ map(str, range(0, requested_gpus))
269
+ )
270
+
271
+ if "path" in app_info["input"]:
272
+ mapped_input = Path(app_info["input"]["path"]).as_posix()
273
+ else:
274
+ mapped_input = DefaultValues.INPUT_DIR
275
+
276
+ if not posixpath.isabs(mapped_input):
277
+ mapped_input = posixpath.join(app_info["workingDirectory"], mapped_input)
278
+ if input_path is not None:
279
+ volumes.append((str(input_path), mapped_input))
280
+
281
+ if "path" in app_info["output"]:
282
+ mapped_output = Path(app_info["output"]["path"]).as_posix()
283
+ else:
284
+ mapped_output = DefaultValues.INPUT_DIR
285
+
286
+ if not posixpath.isabs(mapped_output):
287
+ mapped_output = posixpath.join(app_info["workingDirectory"], mapped_output)
288
+ if output_path is not None:
289
+ volumes.append((str(output_path), mapped_output))
290
+
291
+ for env in app_info["environment"]:
292
+ if env == EnvironmentVariables.HOLOSCAN_INPUT_PATH:
293
+ environment_variables[env] = mapped_input
294
+ elif env == EnvironmentVariables.HOLOSCAN_OUTPUT_PATH:
295
+ environment_variables[env] = mapped_output
296
+ else:
297
+ environment_variables[env] = app_info["environment"][env]
298
+
299
+ # always pass path to config file for Holoscan apps
300
+ if (
301
+ "sdk" in app_info
302
+ and app_info["sdk"] == SdkType.Holoscan.value
303
+ and env == EnvironmentVariables.HOLOSCAN_CONFIG_PATH
304
+ ):
305
+ commands.append("--config")
306
+ commands.append(environment_variables[env])
307
+
308
+ if config is not None:
309
+ if EnvironmentVariables.HOLOSCAN_CONFIG_PATH not in app_info["environment"]:
310
+ raise InvalidManifestError(
311
+ "The application manifest does not contain a required "
312
+ f"environment variable: '{EnvironmentVariables.HOLOSCAN_CONFIG_PATH}'"
313
+ )
314
+ volumes.append(
315
+ (
316
+ str(config),
317
+ app_info["environment"][EnvironmentVariables.HOLOSCAN_CONFIG_PATH],
318
+ )
319
+ )
320
+ logger.info(f"Using user provided configuration file: {config}")
321
+
322
+ logger.debug(
323
+ f"Environment variables: {json.dumps(environment_variables, indent=4, sort_keys=True)}"
324
+ )
325
+ logger.debug(f"Volumes: {json.dumps(volumes, indent=4, sort_keys=True)}")
326
+ logger.debug(f"Shared memory size: {shared_memory_size}")
327
+
328
+ ipc_mode = "host" if shared_memory_size is None else None
329
+ ulimits = [
330
+ "memlock=-1",
331
+ "stack=67108864",
332
+ ]
333
+ additional_devices, group_adds = _additional_devices_to_mount(is_root)
334
+ devices.extend(additional_devices)
335
+
336
+ video_group = run_cmd_output(["/usr/bin/cat", "/etc/group"], "video").split(":")[2]
337
+ if not is_root and video_group not in group_adds:
338
+ group_adds.append(video_group)
339
+
340
+ if terminal:
341
+ _enter_terminal(
342
+ name,
343
+ image_name,
344
+ app_info,
345
+ network,
346
+ user,
347
+ volumes,
348
+ environment_variables,
349
+ shared_memory_size,
350
+ ipc_mode,
351
+ ulimits,
352
+ devices,
353
+ group_adds,
354
+ )
355
+ else:
356
+ _start_container(
357
+ name,
358
+ image_name,
359
+ app_info,
360
+ quiet,
361
+ commands,
362
+ network,
363
+ user,
364
+ volumes,
365
+ environment_variables,
366
+ shared_memory_size,
367
+ ipc_mode,
368
+ ulimits,
369
+ devices,
370
+ group_adds,
371
+ )
372
+
373
+
374
+ def _start_container(
375
+ name,
376
+ image_name,
377
+ app_info,
378
+ quiet,
379
+ commands,
380
+ network,
381
+ user,
382
+ volumes,
383
+ environment_variables,
384
+ shared_memory_size,
385
+ ipc_mode,
386
+ ulimits,
387
+ devices,
388
+ group_adds,
389
+ ):
390
+ container = docker.container.create(
391
+ image_name,
392
+ command=commands,
393
+ envs=environment_variables,
394
+ hostname=name,
395
+ name=name,
396
+ networks=[network],
397
+ remove=True,
398
+ shm_size=shared_memory_size,
399
+ user=user,
400
+ volumes=volumes,
401
+ workdir=app_info["workingDirectory"],
402
+ ipc=ipc_mode,
403
+ cap_add=["CAP_SYS_PTRACE"],
404
+ ulimit=ulimits,
405
+ devices=devices,
406
+ groups_add=group_adds,
407
+ runtime="nvidia",
408
+ )
409
+ container_name = container.name
410
+ container_id = container.id[:12]
411
+
412
+ ulimit_str = ", ".join(
413
+ f"{ulimit.name}={ulimit.soft}:{ulimit.hard}"
414
+ for ulimit in container.host_config.ulimits
415
+ )
416
+ logger.info(
417
+ f"Launching container ({container_id}) using image '{image_name}'..."
418
+ f"\n container name: {container_name}"
419
+ f"\n host name: {container.config.hostname}"
420
+ f"\n network: {network}"
421
+ f"\n user: {container.config.user}"
422
+ f"\n ulimits: {ulimit_str}"
423
+ f"\n cap_add: {', '.join(container.host_config.cap_add)}"
424
+ f"\n ipc mode: {container.host_config.ipc_mode}"
425
+ f"\n shared memory size: {container.host_config.shm_size}"
426
+ f"\n devices: {', '.join(devices)}"
427
+ f"\n group_add: {', '.join(group_adds)}"
428
+ )
429
+ logs = container.start(
430
+ attach=True,
431
+ stream=True,
432
+ )
433
+
434
+ for log in logs:
435
+ if log[0] == "stdout":
436
+ if not quiet:
437
+ print(log[1].decode("utf-8"))
438
+ elif log[0] == "stderr":
439
+ try:
440
+ print(str(log[1].decode("utf-8")))
441
+ except Exception:
442
+ print(str(log[1]))
443
+
444
+ logger.info(f"Container '{container_name}'({container_id}) exited.")
445
+
446
+
447
+ def _enter_terminal(
448
+ name,
449
+ image_name,
450
+ app_info,
451
+ network,
452
+ user,
453
+ volumes,
454
+ environment_variables,
455
+ shared_memory_size,
456
+ ipc_mode,
457
+ ulimits,
458
+ devices,
459
+ group_adds,
460
+ ):
461
+ print("\n\nEntering terminal...")
462
+ print(
463
+ "\n".join(
464
+ f"\t{k:25s}\t{v}"
465
+ for k, v in sorted(environment_variables.items(), key=lambda t: str(t[0]))
466
+ )
467
+ )
468
+ print("\n\n")
469
+ docker.container.run(
470
+ image_name,
471
+ detach=False,
472
+ entrypoint="/bin/bash",
473
+ envs=environment_variables,
474
+ hostname=name,
475
+ interactive=True,
476
+ name=name,
477
+ networks=[network],
478
+ remove=True,
479
+ shm_size=shared_memory_size,
480
+ tty=True,
481
+ user=user,
482
+ volumes=volumes,
483
+ workdir=app_info["workingDirectory"],
484
+ ipc=ipc_mode,
485
+ cap_add=["CAP_SYS_PTRACE"],
486
+ ulimit=ulimits,
487
+ devices=devices,
488
+ groups_add=group_adds,
489
+ runtime="nvidia",
490
+ )
491
+ logger.info("Container exited.")
492
+
493
+
494
+ def _additional_devices_to_mount(is_root: bool):
495
+ """Mounts additional devices"""
496
+ devices = []
497
+ group_adds = []
498
+
499
+ # On iGPU, the /dev/dri/* devices (mounted by the NV container runtime) permissions require root
500
+ # privilege or to be part of the `video` and `render` groups. The ID for these group names might
501
+ # differ on the host system and in the container, so we need to pass the group ID instead of the
502
+ # group name when running docker.
503
+ if (
504
+ os.path.exists("/sys/devices/platform/gpu.0/load")
505
+ and os.path.exists("/usr/bin/tegrastats")
506
+ and not is_root
507
+ ):
508
+ group = run_cmd_output(["/usr/bin/cat", "/etc/group"], "video").split(":")[2]
509
+ group_adds.append(group)
510
+ group = run_cmd_output(["/usr/bin/cat", "/etc/group"], "render").split(":")[2]
511
+ group_adds.append(group)
512
+ return (devices, group_adds)
513
+
514
+
515
+ def _host_is_native_igpu() -> bool:
516
+ proc = subprocess.run(
517
+ ["nvidia-smi", "--query-gpu", "name", "--format=csv,noheader"],
518
+ shell=False,
519
+ capture_output=True,
520
+ )
521
+ return "nvgpu" in str(proc.stdout)
@@ -0,0 +1,49 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ from enum import Enum
16
+
17
+
18
+ class ApplicationType(Enum):
19
+ PythonModule = "Python Module"
20
+ PythonFile = "Python File"
21
+ CppCMake = "C++"
22
+ Binary = "Binary"
23
+
24
+
25
+ class SdkType(Enum):
26
+ """
27
+ Note the values assigned are used as entry_points in setup.py for detecting the SDK to use.
28
+ """
29
+
30
+ Holoscan = "holoscan"
31
+ MonaiDeploy = "monai-deploy"
32
+
33
+
34
+ class Arch(Enum):
35
+ amd64 = "linux/amd64"
36
+ arm64 = "linux/arm64"
37
+
38
+
39
+ class Platform(Enum):
40
+ IGXOrinDevIt = "igx-orin-devkit"
41
+ JetsonAgxOrinDevKit = "jetson-agx-orin-devkit"
42
+ X64Workstation = "x64-workstation"
43
+ SBSA = "sbsa"
44
+
45
+
46
+ class PlatformConfiguration(Enum):
47
+ iGPU = "igpu" # noqa: N815
48
+ dGPU = "dgpu" # noqa: N815
49
+ CPU = "cpu"
@@ -0,0 +1,126 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ class HoloscanSdkError(Exception):
16
+ """Base class for exceptions in this module."""
17
+
18
+ pass
19
+
20
+
21
+ class WrongApplicationPathError(HoloscanSdkError):
22
+ """Raise when wrong application path is specified."""
23
+
24
+ pass
25
+
26
+
27
+ class UnknownApplicationTypeError(HoloscanSdkError):
28
+ """Raise when wrong application path is specified."""
29
+
30
+ pass
31
+
32
+
33
+ class InvalidSdkError(HoloscanSdkError):
34
+ """Raise when the SDK version or SDK file is not supported."""
35
+
36
+ pass
37
+
38
+
39
+ class FailedToDetectSDKVersionError(HoloscanSdkError):
40
+ """Raise when unable to detect the SDK version."""
41
+
42
+ pass
43
+
44
+
45
+ class InvalidApplicationConfigurationError(HoloscanSdkError):
46
+ """
47
+ Raise when required configuration value cannot be found
48
+ in the application configuration files."""
49
+
50
+ pass
51
+
52
+
53
+ class IncompatiblePlatformConfigurationError(HoloscanSdkError):
54
+ """
55
+ Raise when the platforms given by the user are incompatible."""
56
+
57
+ pass
58
+
59
+
60
+ class RunContainerError(HoloscanSdkError):
61
+ """
62
+ Raise when an error is encountered while running the container image."""
63
+
64
+ pass
65
+
66
+
67
+ class InvalidManifestError(HoloscanSdkError):
68
+ """
69
+ Raise when the manifest is invalid."""
70
+
71
+ pass
72
+
73
+
74
+ class ManifestReadError(HoloscanSdkError):
75
+ """
76
+ Raise when the manifest is invalid."""
77
+
78
+
79
+ class ExternalAssetDownloadError(HoloscanSdkError):
80
+ """
81
+ Raise when the manifest is invalid."""
82
+
83
+
84
+ class InvalidSourceFileError(HoloscanSdkError):
85
+ """
86
+ Raise when the provided artifact source file is invalid."""
87
+
88
+ pass
89
+
90
+
91
+ class InvalidTagValueError(HoloscanSdkError):
92
+ """
93
+ Raise when the Docker tag is invalid."""
94
+
95
+ pass
96
+
97
+
98
+ class InvalidSharedMemoryValueError(HoloscanSdkError):
99
+ """
100
+ Raise when the shared memory value is invalid."""
101
+
102
+ pass
103
+
104
+
105
+ class ManifestDownloadError(HoloscanSdkError):
106
+ """
107
+ Raise when the failed to download manifest file."""
108
+
109
+ pass
110
+
111
+
112
+ class UnmatchedDeviceError(HoloscanSdkError):
113
+ """
114
+ Raise when the shared memory value is invalid."""
115
+
116
+ def __init__(self, unmatched_devices: list[str], *args: object) -> None:
117
+ super().__init__(
118
+ f"The following devices cannot be found in /dev/: {str.join(',', unmatched_devices)}"
119
+ )
120
+
121
+
122
+ class GpuResourceError(HoloscanSdkError):
123
+ """
124
+ Raise when the available GPUs are less than requetsed."""
125
+
126
+ pass