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,426 @@
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 logging
16
+ from argparse import Namespace
17
+ from pathlib import Path
18
+ from typing import Optional, Union
19
+
20
+ from ..common.artifact_sources import ArtifactSources
21
+ from ..common.constants import Constants
22
+ from ..common.dockerutils import image_exists
23
+ from ..common.enum_types import ApplicationType, SdkType
24
+ from ..common.exceptions import IncompatiblePlatformConfigurationError, InvalidSdkError
25
+ from ..common.sdk_utils import (
26
+ detect_sdk,
27
+ detect_sdk_version,
28
+ validate_holoscan_sdk_version,
29
+ )
30
+ from .parameters import PlatformParameters
31
+
32
+
33
+ class Platform:
34
+ def __init__(self, artifact_sources: ArtifactSources) -> None:
35
+ """
36
+ Args:
37
+ args (Namespace): Input arguments for Packager from CLI
38
+ """
39
+ self._logger = logging.getLogger("packager")
40
+ self._artifact_sources = artifact_sources
41
+
42
+ def configure_platforms(
43
+ self,
44
+ args: Namespace,
45
+ temp_dir: str,
46
+ version: str,
47
+ application_type: ApplicationType,
48
+ ) -> tuple[SdkType, str, str, list[PlatformParameters]]:
49
+ """Configures a list of platforms that need to be built.
50
+ 1. Detect the SDK to use
51
+ 2. Detect the version of the SDK to use
52
+ 3. Ensure user provided arguments are valid and do not conflict
53
+ 4. Builds a list of platforms to be built where each platform includes the following:
54
+ a. SDK distribution file(s) to use
55
+ b. Base image to use
56
+ c. Build image to use if application type is C++
57
+
58
+ Args:
59
+ args (Namespace): user provided arguments
60
+ temp_dir (str): temporary path for storing downloaded artifacts
61
+ version (str): application version
62
+ application_type (ApplicationType): application type
63
+
64
+ Returns:
65
+ Tuple[SdkType, str, str, List[PlatformParameters]]: A tuple contains SDK, Holoscan SDK
66
+ version, MONAI Deploy App SDK version and list of platforms to be built
67
+ """
68
+ sdk = detect_sdk(args.sdk)
69
+ holoscan_sdk_version, monai_deploy_app_sdk_version = detect_sdk_version(
70
+ sdk, args.sdk_version
71
+ )
72
+
73
+ validate_holoscan_sdk_version(self._artifact_sources, holoscan_sdk_version)
74
+
75
+ self._validate_platform_options(args, sdk)
76
+
77
+ platforms = []
78
+ for platform in args.platform:
79
+ platform_config = args.platform_config
80
+
81
+ platform_parameters = PlatformParameters(
82
+ platform, platform_config, args.tag, version
83
+ )
84
+
85
+ (
86
+ platform_parameters.custom_base_image,
87
+ platform_parameters.base_image,
88
+ ) = self._find_base_image(
89
+ platform_parameters, holoscan_sdk_version, args.base_image
90
+ )
91
+
92
+ platform_parameters.build_image = self._find_build_image(
93
+ platform_parameters,
94
+ holoscan_sdk_version,
95
+ application_type,
96
+ args.build_image,
97
+ )
98
+
99
+ (
100
+ (
101
+ platform_parameters.custom_holoscan_sdk,
102
+ platform_parameters.holoscan_sdk_file,
103
+ ),
104
+ (
105
+ platform_parameters.custom_monai_deploy_sdk,
106
+ platform_parameters.monai_deploy_sdk_file,
107
+ ),
108
+ ) = self._select_sdk_file(
109
+ platform_parameters,
110
+ temp_dir,
111
+ sdk,
112
+ holoscan_sdk_version,
113
+ monai_deploy_app_sdk_version,
114
+ application_type,
115
+ args.holoscan_sdk_file,
116
+ args.monai_deploy_sdk_file,
117
+ )
118
+
119
+ if sdk is SdkType.Holoscan:
120
+ platform_parameters.health_probe = self._artifact_sources.health_probe(
121
+ holoscan_sdk_version
122
+ )[platform_parameters.platform_arch.value]
123
+
124
+ platforms.append(platform_parameters)
125
+
126
+ return (sdk, holoscan_sdk_version, monai_deploy_app_sdk_version, platforms)
127
+
128
+ def _validate_platform_options(self, args: Namespace, sdk: SdkType):
129
+ """Validates user requests.
130
+ - Packager accepts a single SDK distribution file, if user requests to build x64 and arm64
131
+ at the same time, raise exception
132
+ - If Packager was called using 'holoscan' command while user provides a MONAI Deploy APP
133
+ SDK file, raise exception
134
+
135
+ Args:
136
+ args (Namespace): user provided arguments
137
+ sdk (SdkType): SDK type
138
+
139
+ Raises:
140
+ IncompatiblePlatformConfigurationError: when validation fails
141
+ """
142
+ if (
143
+ args.holoscan_sdk_file is not None or args.monai_deploy_sdk_file is not None
144
+ ) and len(args.platform) > 1:
145
+ raise IncompatiblePlatformConfigurationError(
146
+ "Validation error: '--sdk-file' cannot be used with multiple platforms."
147
+ )
148
+ if sdk == SdkType.Holoscan and args.monai_deploy_sdk_file is not None:
149
+ raise IncompatiblePlatformConfigurationError(
150
+ "--monai-deploy-sdk-file was used. Did you mean to use "
151
+ "'monai-deploy package' command instead?"
152
+ )
153
+
154
+ def _find_base_image(
155
+ self,
156
+ platform_parameters: PlatformParameters,
157
+ sdk_version: str,
158
+ base_image: Optional[str] = None,
159
+ ) -> tuple[bool, str]:
160
+ """
161
+ Ensure user provided base image exists in Docker or locate the base image to use based on
162
+ request platform.
163
+
164
+ Args:
165
+ platform_parameters (PlatformParameters): target platform parameters
166
+ sdk_version (str): SDK version
167
+ base_image (Optional[str]): user provided base image
168
+
169
+ Returns:
170
+ (Tuple(bool, str)): bool: True if using user provided image.
171
+ str: base image for building the image based on the given
172
+ platform and SDK version.
173
+ """
174
+ if base_image is not None:
175
+ if image_exists(base_image):
176
+ return (True, base_image)
177
+ else:
178
+ raise InvalidSdkError(
179
+ f"Specified base image cannot be found: {base_image}"
180
+ )
181
+
182
+ try:
183
+ return (
184
+ False,
185
+ self._artifact_sources.base_image(sdk_version)[
186
+ platform_parameters.platform_config.value
187
+ ],
188
+ )
189
+ except Exception as ex:
190
+ raise IncompatiblePlatformConfigurationError(
191
+ f"""No base image found for the selected configuration:
192
+ Platform: {platform_parameters.platform}
193
+ Configuration: {platform_parameters.platform_config}
194
+ Version: {sdk_version}"""
195
+ ) from ex
196
+
197
+ def _find_build_image(
198
+ self,
199
+ platform_parameters: PlatformParameters,
200
+ sdk_version: str,
201
+ application_type: ApplicationType,
202
+ build_image: Optional[str] = None,
203
+ ) -> Optional[str]:
204
+ """
205
+ Ensure user provided build image exists or locate the build image to use based on the
206
+ requested platform.
207
+
208
+ Args:
209
+ platform_parameters (PlatformParameters): target platform parameters
210
+ sdk_version (str): SDK version
211
+ application_type (ApplicationType): application type
212
+ build_image (Optional[str]): user provided build image
213
+
214
+ Returns:
215
+ (str): build image for building the image based on the given platform and SDK version.
216
+ """
217
+ if build_image is not None:
218
+ if image_exists(build_image):
219
+ return build_image
220
+ else:
221
+ raise InvalidSdkError(
222
+ f"Specified build image cannot be found: {build_image}"
223
+ )
224
+
225
+ if application_type == ApplicationType.CppCMake:
226
+ try:
227
+ return self._artifact_sources.build_images(sdk_version)[
228
+ platform_parameters.platform_config.value
229
+ ][platform_parameters.platform.value]
230
+ except Exception as ex:
231
+ raise IncompatiblePlatformConfigurationError(
232
+ f"No build image found for the selected configuration:"
233
+ f"\n Platform: {platform_parameters.platform.value}"
234
+ f"\n Configuration: {platform_parameters.platform_config.value}"
235
+ f"\n Version: {sdk_version}"
236
+ ) from ex
237
+ else:
238
+ return None
239
+
240
+ def _select_sdk_file(
241
+ self,
242
+ platform_parameters: PlatformParameters,
243
+ temp_dir: str,
244
+ sdk: SdkType,
245
+ holoscan_sdk_version: str,
246
+ monai_deploy_app_sdk_version: Optional[str],
247
+ application_type: ApplicationType,
248
+ holoscan_sdk_file: Optional[Path] = None,
249
+ monai_deploy_sdk_file: Optional[Path] = None,
250
+ ) -> tuple[
251
+ tuple[bool, Union[Path, str]],
252
+ tuple[
253
+ Union[Optional[Path], Optional[str]], Union[Optional[Path], Optional[str]]
254
+ ],
255
+ ]:
256
+ """
257
+ Detects the SDK distributable to use based on internal mapping or user input.
258
+
259
+ - C++ & binary applications, attempt to download the SDK file.
260
+ - Python application, use Holoscan or MONAI Deploy App SDK PyPI package.
261
+
262
+ Args:
263
+ platform_parameters (PlatformParameters): target platform parameters
264
+ temp_dir (str): temporary location for storing downloaded files.
265
+ sdk (SdkType): SDK to use
266
+ holoscan_sdk_version (str): Holoscan SDK version
267
+ monai_deploy_app_sdk_version (Optional[str]): MONAI Deploy SDK version
268
+ application_type (ApplicationType): application type
269
+ holoscan_sdk_file (Optional[Path]): path to the user specified Holoscan SDK file
270
+ monai_deploy_sdk_file (Optional[Path]): path to the user specified MONAI Deploy
271
+ App SDK file
272
+
273
+ Returns:
274
+ (Tuple[Union[Path, str], Union[Path, str, None]]): A tuple where the first value
275
+ contains Holoscan SDK and the second value contains MONAI Deploy App SDK.
276
+
277
+ Raises:
278
+ InvalidSdkError: when user specified SDK file does not pass validation.
279
+ """
280
+ if sdk == SdkType.Holoscan:
281
+ return (
282
+ self._get_holoscan_sdk(
283
+ platform_parameters,
284
+ temp_dir,
285
+ SdkType.Holoscan,
286
+ holoscan_sdk_version,
287
+ application_type,
288
+ holoscan_sdk_file,
289
+ ),
290
+ (None, None),
291
+ )
292
+ elif sdk == SdkType.MonaiDeploy:
293
+ if monai_deploy_app_sdk_version is None:
294
+ raise InvalidSdkError("MONAI Deploy App SDK version missing")
295
+ return (
296
+ self._get_holoscan_sdk(
297
+ platform_parameters,
298
+ temp_dir,
299
+ SdkType.Holoscan,
300
+ holoscan_sdk_version,
301
+ application_type,
302
+ holoscan_sdk_file,
303
+ ),
304
+ self._get_monai_deploy_sdk(
305
+ monai_deploy_app_sdk_version, monai_deploy_sdk_file
306
+ ),
307
+ )
308
+ return (None, None)
309
+
310
+ def _get_holoscan_sdk(
311
+ self,
312
+ platform_parameters: PlatformParameters,
313
+ temp_dir: str,
314
+ sdk: SdkType,
315
+ sdk_version: str,
316
+ application_type: ApplicationType,
317
+ sdk_file: Optional[Path] = None,
318
+ ) -> tuple[bool, Union[Path, str]]:
319
+ """
320
+ Validates Holoscan SDK redistributable file if specified.
321
+ Otherwise, attempt to download the SDK file from internet.
322
+
323
+ Args:
324
+ platform_parameters (PlatformParameters): Platform parameters
325
+ temp_dir (str): Temporary location for storing downloaded distribution files.
326
+ sdk (SdkType): SDK for building the application
327
+ sdk_version (str): SDK version. Defaults to None.
328
+ application_type (ApplicationType): application type
329
+ sdk_file (Optional[Path], optional): SDK file from user input. Defaults to None.
330
+
331
+ Raises:
332
+ InvalidSdkError: when an invalid SDK file is provided or unable to find matching SDK
333
+ file.
334
+
335
+ Returns:
336
+ Tuple[bool, Union[Path, str]]:
337
+ bool: True when user provides SDk file. Otherwise, False.
338
+ Union[Path, str]: User provided SDK file path or package version.
339
+ """
340
+ assert sdk is SdkType.Holoscan
341
+
342
+ if sdk_file is not None:
343
+ if application_type in [
344
+ ApplicationType.PythonModule,
345
+ ApplicationType.PythonFile,
346
+ ]:
347
+ if sdk_file.suffix not in Constants.PYPI_FILE_EXTENSIONS:
348
+ raise InvalidSdkError(
349
+ "Invalid SDK file format, must be a PyPI wheel file with .whl file "
350
+ "extension."
351
+ )
352
+ return (True, sdk_file)
353
+ elif application_type in [
354
+ ApplicationType.CppCMake,
355
+ ApplicationType.Binary,
356
+ ]:
357
+ if sdk_file.suffix != Constants.DEBIAN_FILE_EXTENSION:
358
+ raise InvalidSdkError(
359
+ "Invalid SDK file format, must be a Debian package file with .deb "
360
+ "file extension."
361
+ )
362
+ return (True, sdk_file)
363
+
364
+ raise InvalidSdkError(f"Unknown application type: {application_type.value}")
365
+ else:
366
+ if application_type in [
367
+ ApplicationType.PythonModule,
368
+ ApplicationType.PythonFile,
369
+ ]:
370
+ wheel_package_version = self._artifact_sources.wheel_package_version(
371
+ sdk_version
372
+ )
373
+
374
+ if wheel_package_version is None:
375
+ raise InvalidSdkError(
376
+ "Unable to locate matching Holoscan SDK PyPI package with "
377
+ f"version {sdk_version}."
378
+ )
379
+
380
+ return (False, wheel_package_version)
381
+ elif application_type in [
382
+ ApplicationType.CppCMake,
383
+ ApplicationType.Binary,
384
+ ]:
385
+ debian_package_version = self._artifact_sources.debian_package_version(
386
+ sdk_version
387
+ )
388
+
389
+ if debian_package_version is None:
390
+ raise InvalidSdkError(
391
+ "Unable to locate matching Holoscan SDK Debian package with "
392
+ f"version {sdk_version}."
393
+ )
394
+
395
+ return (False, debian_package_version)
396
+
397
+ raise InvalidSdkError(f"Unknown application type: {application_type.value}")
398
+
399
+ def _get_monai_deploy_sdk(
400
+ self,
401
+ monai_deploy_app_sdk_version: Optional[str],
402
+ sdk_file: Optional[Path] = None,
403
+ ) -> tuple[bool, Union[Optional[Path], Optional[str]]]:
404
+ """
405
+ Validates MONAI Deploy SDK redistributable file if specified.
406
+ Otherwise, Docker build stage will install the SDK from PyPI.
407
+
408
+ Args:
409
+ sdk_file (Optional[Path], optional): SDK file from user input. Defaults to None.
410
+ monai_deploy_app_sdk_version: (Optional[str]): MONAI Deploy App SDK version
411
+ Raises:
412
+ InvalidSdkError: when an invalid SDK file is provided or unable to find matching SDK
413
+ file.
414
+
415
+ Returns:
416
+ Union[Path, str]: Path to the SDK redistributable file.
417
+ """
418
+
419
+ if sdk_file is not None:
420
+ if sdk_file.suffix not in Constants.PYPI_FILE_EXTENSIONS:
421
+ raise InvalidSdkError(
422
+ "Invalid SDK file format, must be a PyPI wheel file with .whl file extension."
423
+ )
424
+ return (True, sdk_file)
425
+
426
+ return (False, None)