holoscan-cli 2.9.0__py3-none-any.whl → 3.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.
holoscan_cli/__main__.py CHANGED
@@ -20,7 +20,6 @@ import os
20
20
  from pathlib import Path
21
21
  from typing import Optional, Union
22
22
 
23
- from .common.enum_types import Platform, PlatformConfiguration
24
23
 
25
24
  logging.getLogger("docker.api.build").setLevel(logging.WARNING)
26
25
  logging.getLogger("docker.auth").setLevel(logging.WARNING)
@@ -97,14 +96,6 @@ def parse_args(argv: Optional[list[str]] = None) -> argparse.Namespace:
97
96
  parser.print_help()
98
97
  parser.exit()
99
98
 
100
- if args.command == "package":
101
- if args.platform[0] == Platform.X64Workstation:
102
- args.platform_config = PlatformConfiguration.dGPU
103
- elif args.platform_config is None:
104
- parser.error(
105
- f"'--platform-config' is required for '{args.platform[0].value}'"
106
- )
107
-
108
99
  return args
109
100
 
110
101
 
@@ -15,7 +15,6 @@
15
15
  import argparse
16
16
  import os
17
17
  from pathlib import Path
18
-
19
18
  from .constants import SDK
20
19
  from .enum_types import Platform, PlatformConfiguration, SdkType
21
20
 
@@ -107,6 +106,7 @@ def valid_platforms(platforms_str: str) -> list[Platform]:
107
106
  platforms = platforms_str.lower().split(",")
108
107
  platform_enums = []
109
108
  for platform in platforms:
109
+ platform = platform.strip()
110
110
  if platform not in SDK.PLATFORMS:
111
111
  raise argparse.ArgumentTypeError(
112
112
  f"{platform} is not a valid option for --platforms."
@@ -128,7 +128,7 @@ def valid_platform_config(platform_config_str: str) -> PlatformConfiguration:
128
128
  Otherwise, raises argparse.ArgumentTypeError.
129
129
  """
130
130
 
131
- platform_config_str = platform_config_str.lower()
131
+ platform_config_str = platform_config_str.lower().strip()
132
132
  if platform_config_str not in SDK.PLATFORM_CONFIGS:
133
133
  raise argparse.ArgumentTypeError(
134
134
  f"{platform_config_str} is not a valid option for --platform-config."
@@ -149,8 +149,23 @@ def valid_sdk_type(sdk_str: str) -> SdkType:
149
149
  Otherwise, raises argparse.ArgumentTypeError.
150
150
  """
151
151
 
152
- sdk_str = sdk_str.lower()
152
+ sdk_str = sdk_str.lower().strip()
153
153
  if sdk_str not in SDK.SDKS:
154
154
  raise argparse.ArgumentTypeError(f"{sdk_str} is not a valid option for --sdk.")
155
155
 
156
156
  return SdkType(sdk_str)
157
+
158
+
159
+ def valid_host_ip(host_ip: str) -> str:
160
+ """Helper type checking and type converting method for ArgumentParser.add_argument
161
+ to convert check valid host:ip format.
162
+
163
+ Args:
164
+ host_ip: host ip string
165
+ """
166
+
167
+ host, ip = host_ip.split(":")
168
+ if host == "" or ip == "":
169
+ raise argparse.ArgumentTypeError(f"Invalid valid for --add-host: '{host_ip}'")
170
+
171
+ return host_ip
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
  import json
16
16
  import logging
17
+ import os
17
18
  from typing import Any, Optional
18
19
 
19
20
  import requests
@@ -36,8 +37,9 @@ class ArtifactSources:
36
37
  HoloscanVersion = None
37
38
  ManifestFileUrl = None
38
39
 
39
- def __init__(self) -> None:
40
+ def __init__(self, cuda_version: int) -> None:
40
41
  self._logger = logging.getLogger("common")
42
+ self._cuda_version = cuda_version
41
43
  try:
42
44
  ArtifactSources.HoloscanVersion = ".".join(
43
45
  str(i) for i in Version(__version__).release[0:3]
@@ -48,7 +50,10 @@ class ArtifactSources:
48
50
  "a Holoscan SDK version to use."
49
51
  ) from ex
50
52
 
51
- ArtifactSources.ManifestFileUrl = f"https://raw.githubusercontent.com/nvidia-holoscan/holoscan-cli/refs/heads/main/releases/{ArtifactSources.HoloscanVersion}/artifacts.json" # noqa: E501
53
+ if self._cuda_version == 12:
54
+ ArtifactSources.ManifestFileUrl = f"https://raw.githubusercontent.com/nvidia-holoscan/holoscan-cli/refs/heads/main/releases/{ArtifactSources.HoloscanVersion}/artifacts-cu12.json" # noqa: E501
55
+ else:
56
+ ArtifactSources.ManifestFileUrl = f"https://raw.githubusercontent.com/nvidia-holoscan/holoscan-cli/refs/heads/main/releases/{ArtifactSources.HoloscanVersion}/artifacts.json" # noqa: E501
52
57
 
53
58
  @property
54
59
  def holoscan_versions(self) -> list[str]:
@@ -82,6 +87,12 @@ class ArtifactSources:
82
87
  "Downloading manifest files from non-HTTPS servers is not supported."
83
88
  )
84
89
  else:
90
+ if os.path.isdir(uri):
91
+ if self._cuda_version == 12:
92
+ uri = os.path.join(uri, "artifacts-cu12.json")
93
+ else:
94
+ uri = os.path.join(uri, "artifacts.json")
95
+
85
96
  self._logger.info(f"Using CLI manifest file from {uri}...")
86
97
  with open(uri) as file:
87
98
  temp = json.load(file)
@@ -116,7 +127,7 @@ class ArtifactSources:
116
127
  )
117
128
 
118
129
  def _download_manifest_internal(self, url, headers=None):
119
- self._logger.info("Downloading CLI manifest file...")
130
+ self._logger.info(f"Downloading CLI manifest file from {url}...")
120
131
  manifest = requests.get(url, headers=headers)
121
132
 
122
133
  try:
@@ -69,6 +69,9 @@ class Constants:
69
69
 
70
70
  RESOURCE_SHARED_MEMORY_KEY = "sharedMemory"
71
71
 
72
+ # Include "holoscan" for backward compatibility; remove post 3.7.0 release
73
+ PYPI_PACKAGE_NAMES = ["holoscan-cu12", "holoscan-cu13", "holoscan"]
74
+
72
75
 
73
76
  class SDK:
74
77
  """
@@ -77,19 +80,21 @@ class SDK:
77
80
  """
78
81
 
79
82
  # Platform to architecture mappings
80
- PLATFORM_MAPPINGS = {
81
- Platform.IGXOrinDevIt: Arch.arm64,
82
- Platform.JetsonAgxOrinDevKit: Arch.arm64,
83
+ PLATFORM_ARCH_MAPPINGS = {
84
+ Platform.Jetson: Arch.arm64,
85
+ Platform.IGX_iGPU: Arch.arm64,
86
+ Platform.IGX_dGPU: Arch.arm64,
83
87
  Platform.SBSA: Arch.arm64,
84
- Platform.X64Workstation: Arch.amd64,
88
+ Platform.x86_64: Arch.amd64,
85
89
  }
86
90
 
87
91
  # Values of all platforms supported by the Packager
88
92
  PLATFORMS = [
89
- Platform.IGXOrinDevIt.value,
90
- Platform.JetsonAgxOrinDevKit.value,
93
+ Platform.Jetson.value,
94
+ Platform.IGX_iGPU.value,
95
+ Platform.IGX_dGPU.value,
91
96
  Platform.SBSA.value,
92
- Platform.X64Workstation.value,
97
+ Platform.x86_64.value,
93
98
  ]
94
99
 
95
100
  # Values of all platform configurations supported by the Packager
@@ -98,6 +103,14 @@ class SDK:
98
103
  PlatformConfiguration.dGPU.value,
99
104
  ]
100
105
 
106
+ INTERNAL_PLATFORM_MAPPINGS = {
107
+ Platform.Jetson: (Platform.JetsonAgxOrinDevKit, PlatformConfiguration.iGPU),
108
+ Platform.IGX_iGPU: (Platform.IGXOrinDevIt, PlatformConfiguration.iGPU),
109
+ Platform.IGX_dGPU: (Platform.IGXOrinDevIt, PlatformConfiguration.dGPU),
110
+ Platform.SBSA: (Platform.SBSA, PlatformConfiguration.dGPU),
111
+ Platform.x86_64: (Platform.X64Workstation, PlatformConfiguration.dGPU),
112
+ }
113
+
101
114
  # Values of SDKs supported by the Packager
102
115
  SDKS = [SdkType.Holoscan.value, SdkType.MonaiDeploy.value]
103
116
 
@@ -172,6 +172,7 @@ def docker_run(
172
172
  platform_config: str,
173
173
  shared_memory_size: str = "1GB",
174
174
  is_root: bool = False,
175
+ remove: bool = False,
175
176
  ):
176
177
  """Creates and runs a Docker container
177
178
 
@@ -351,6 +352,7 @@ def docker_run(
351
352
  ulimits,
352
353
  devices,
353
354
  group_adds,
355
+ remove,
354
356
  )
355
357
  else:
356
358
  _start_container(
@@ -368,6 +370,7 @@ def docker_run(
368
370
  ulimits,
369
371
  devices,
370
372
  group_adds,
373
+ remove,
371
374
  )
372
375
 
373
376
 
@@ -386,6 +389,7 @@ def _start_container(
386
389
  ulimits,
387
390
  devices,
388
391
  group_adds,
392
+ remove,
389
393
  ):
390
394
  container = docker.container.create(
391
395
  image_name,
@@ -394,7 +398,7 @@ def _start_container(
394
398
  hostname=name,
395
399
  name=name,
396
400
  networks=[network],
397
- remove=True,
401
+ remove=False,
398
402
  shm_size=shared_memory_size,
399
403
  user=user,
400
404
  volumes=volumes,
@@ -441,7 +445,18 @@ def _start_container(
441
445
  except Exception:
442
446
  print(str(log[1]))
443
447
 
444
- logger.info(f"Container '{container_name}'({container_id}) exited.")
448
+ exit_code = container.state.exit_code
449
+ logger.info(
450
+ f"Container '{container_name}'({container_id}) exited with code {exit_code}."
451
+ )
452
+
453
+ if remove:
454
+ container.remove()
455
+
456
+ if exit_code != 0:
457
+ raise RuntimeError(
458
+ f"Container '{container_name}'({container_id}) exited with code {exit_code}."
459
+ )
445
460
 
446
461
 
447
462
  def _enter_terminal(
@@ -457,6 +472,7 @@ def _enter_terminal(
457
472
  ulimits,
458
473
  devices,
459
474
  group_adds,
475
+ remove,
460
476
  ):
461
477
  print("\n\nEntering terminal...")
462
478
  print(
@@ -475,7 +491,7 @@ def _enter_terminal(
475
491
  interactive=True,
476
492
  name=name,
477
493
  networks=[network],
478
- remove=True,
494
+ remove=remove,
479
495
  shm_size=shared_memory_size,
480
496
  tty=True,
481
497
  user=user,
@@ -37,10 +37,18 @@ class Arch(Enum):
37
37
 
38
38
 
39
39
  class Platform(Enum):
40
+ # User values - when a new value is added, please also update SDK.PLATFORMS
41
+ Jetson = "jetson"
42
+ IGX_iGPU = "igx-igpu"
43
+ IGX_dGPU = "igx-dgpu"
44
+ SBSA = "sbsa"
45
+ x86_64 = "x86_64"
46
+
47
+ # Internal use only - for mapping actual platform with platform configuration
48
+ # as defined in SDK.INTERNAL_PLATFORM_MAPPINGS.
40
49
  IGXOrinDevIt = "igx-orin-devkit"
41
50
  JetsonAgxOrinDevKit = "jetson-agx-orin-devkit"
42
51
  X64Workstation = "x64-workstation"
43
- SBSA = "sbsa"
44
52
 
45
53
 
46
54
  class PlatformConfiguration(Enum):
@@ -21,6 +21,7 @@ from typing import Optional
21
21
  from packaging.version import Version
22
22
 
23
23
  from .artifact_sources import ArtifactSources
24
+ from .constants import Constants
24
25
  from .enum_types import SdkType
25
26
  from .exceptions import FailedToDetectSDKVersionError, InvalidSdkError
26
27
 
@@ -72,19 +73,7 @@ def detect_sdk(sdk: Optional[SdkType] = None) -> SdkType:
72
73
 
73
74
  command = None
74
75
  try:
75
- # For Python 3.10+, this check is to support use of the holoscan cli bash script bundled
76
- # with the Debian package.
77
- # Since the Debian package bundles with 3.10, we don't need to handle 3.9 but
78
- # we still need to check if `orig_argv` is supported to avoid breaking unit test.
79
- if (
80
- getattr(sys, "orig_argv", None)
81
- and len(sys.orig_argv) >= 3
82
- and sys.orig_argv[0] == "python3"
83
- and sys.orig_argv[2] == "holoscan_cli"
84
- ):
85
- command = "holoscan"
86
- else:
87
- command = Path(sys.argv[0]).name.lower()
76
+ command = Path(sys.argv[0]).name.lower()
88
77
  return SdkType(command)
89
78
  except Exception as ex:
90
79
  raise InvalidSdkError(f"Invalid SDK value provided: {command}") from ex
@@ -137,32 +126,37 @@ def detect_holoscan_version(sdk_version: Optional[Version] = None) -> str:
137
126
  if sdk_version is not None:
138
127
  return sdk_version.base_version
139
128
  else:
140
- try:
141
- ver_str = importlib.metadata.version("holoscan-cli").title()
142
- ver = Version(ver_str)
143
- ver_str = ".".join(str(i) for i in ver.release)
144
-
145
- if len(ver.release) == 1 and ver.major == ver.release[0]:
146
- ver_str = ver_str + ".0.0"
147
- elif (
148
- len(ver.release) == 2
149
- and ver.major == ver.release[0]
150
- and ver.minor == ver.release[1]
151
- ):
152
- ver_str = ver_str + ".0"
153
- elif (
154
- len(ver.release) == 4
155
- and ver.major == ver.release[0]
156
- and ver.minor == ver.release[1]
157
- and ver.micro == ver.release[2]
158
- ):
159
- ver_str = f"{ver.release[0]}.{ver.release[1]}.{ver.release[2]}"
160
-
161
- return ver_str
162
- except Exception as ex:
129
+ # Scan for installed packages with prefix "holoscan"
130
+ holoscan_pkgs = [
131
+ dist.metadata["Name"]
132
+ for dist in importlib.metadata.distributions()
133
+ if dist.metadata["Name"].lower() in Constants.PYPI_PACKAGE_NAMES
134
+ ]
135
+ if not holoscan_pkgs:
163
136
  raise FailedToDetectSDKVersionError(
164
- "Failed to detect installed Holoscan PyPI version.", ex
165
- ) from ex
137
+ "No installed Holoscan PyPI package found."
138
+ )
139
+ ver_str = importlib.metadata.version(holoscan_pkgs[0]).title()
140
+ ver = Version(ver_str)
141
+ ver_str = ".".join(str(i) for i in ver.release)
142
+
143
+ if len(ver.release) == 1 and ver.major == ver.release[0]:
144
+ ver_str = ver_str + ".0.0"
145
+ elif (
146
+ len(ver.release) == 2
147
+ and ver.major == ver.release[0]
148
+ and ver.minor == ver.release[1]
149
+ ):
150
+ ver_str = ver_str + ".0"
151
+ elif (
152
+ len(ver.release) == 4
153
+ and ver.major == ver.release[0]
154
+ and ver.minor == ver.release[1]
155
+ and ver.micro == ver.release[2]
156
+ ):
157
+ ver_str = f"{ver.release[0]}.{ver.release[1]}.{ver.release[2]}"
158
+
159
+ return ver_str
166
160
 
167
161
 
168
162
  def detect_monaideploy_version(sdk_version: Optional[Version] = None) -> str:
@@ -193,3 +187,17 @@ def detect_monaideploy_version(sdk_version: Optional[Version] = None) -> str:
193
187
  raise FailedToDetectSDKVersionError(
194
188
  "Failed to detect installed MONAI Deploy App SDK PyPI version.", ex
195
189
  ) from ex
190
+
191
+
192
+ def detect_holoscan_cli_version() -> str:
193
+ """
194
+ Detects Holoscan CLI version to use based on installed PyPI package.
195
+ """
196
+ try:
197
+ ver_str = importlib.metadata.version("holoscan-cli").title()
198
+ ver = Version(ver_str)
199
+ return ver.base_version
200
+ except Exception as ex:
201
+ raise FailedToDetectSDKVersionError(
202
+ "Failed to detect installed Holoscan CLI PyPI version.", ex
203
+ ) from ex
holoscan_cli/nics/nics.py CHANGED
@@ -28,6 +28,6 @@ def execute_nics_command(args: Namespace):
28
28
  strs = [f"\n\t{item[0]:<15} : {item[1]}" for item in ip_addresses]
29
29
  print(f"Available network interface cards/IP addresses: \n{''.join(strs)}")
30
30
  except Exception as ex:
31
- logging.error("Error executing nics command.")
31
+ logger.error("Error executing nics command.")
32
32
  logger.debug(ex)
33
33
  sys.exit(4)
@@ -54,7 +54,7 @@ class PackagingArguments:
54
54
 
55
55
  self._platforms: list[PlatformParameters]
56
56
  self._build_parameters = PackageBuildParameters()
57
- self._artifact_sources = ArtifactSources()
57
+ self._artifact_sources = ArtifactSources(args.cuda)
58
58
 
59
59
  if args.source is not None:
60
60
  self._artifact_sources.load(args.source)
@@ -76,6 +76,8 @@ class PackagingArguments:
76
76
  self.build_parameters.cmake_args = args.cmake_args
77
77
  self.build_parameters.includes = args.includes
78
78
  self.build_parameters.additional_libs = args.additional_libs
79
+ self.build_parameters.add_hosts = args.add_hosts
80
+ self.build_parameters.input_data = args.input_data
79
81
 
80
82
  models = Models()
81
83
  platform = Platform(self._artifact_sources)
@@ -127,7 +129,9 @@ class PackagingArguments:
127
129
  self.build_parameters.monai_deploy_app_sdk_version
128
130
  )
129
131
 
130
- self._package_manifest.platform_config = args.platform_config.value
132
+ self._package_manifest.platform_config = self._platforms[
133
+ 0
134
+ ].platform_config.value
131
135
 
132
136
  def _read_application_config_file(self, config_file_path: Path):
133
137
  self._logger.info(
@@ -27,7 +27,10 @@ from ..common.dockerutils import (
27
27
  create_and_get_builder,
28
28
  docker_export_tarball,
29
29
  )
30
- from ..common.exceptions import WrongApplicationPathError
30
+ from ..common.exceptions import (
31
+ IncompatiblePlatformConfigurationError,
32
+ WrongApplicationPathError,
33
+ )
31
34
  from .parameters import PackageBuildParameters, PlatformBuildResults, PlatformParameters
32
35
 
33
36
 
@@ -58,6 +61,8 @@ class BuilderBase:
58
61
  self._copy_model_files()
59
62
  self._copy_docs()
60
63
  self._copy_libs()
64
+ self._copy_input_data()
65
+
61
66
  _ = self._write_dockerignore()
62
67
  _ = self._copy_script()
63
68
 
@@ -88,7 +93,6 @@ class BuilderBase:
88
93
  Returns:
89
94
  PlatformBuildResults: build results
90
95
  """
91
- self.print_build_info(platform_parameters)
92
96
  builder = create_and_get_builder(Constants.LOCAL_BUILDX_BUILDER_NAME)
93
97
 
94
98
  build_result = PlatformBuildResults(platform_parameters)
@@ -117,9 +121,15 @@ class BuilderBase:
117
121
  "tags": [platform_parameters.tag],
118
122
  }
119
123
 
124
+ if self._build_parameters.add_hosts:
125
+ builds["add_hosts"] = {}
126
+ for host in self._build_parameters.add_hosts:
127
+ host_name, host_ip = host.split(":")
128
+ builds["add_hosts"][host_name] = host_ip
129
+
120
130
  export_to_tar_ball = False
121
131
  if self._build_parameters.tarball_output is not None:
122
- build_result.tarball_filenaem = str(
132
+ build_result.tarball_filename = str(
123
133
  self._build_parameters.tarball_output
124
134
  / f"{platform_parameters.tag}{Constants.TARBALL_FILE_EXTENSION}"
125
135
  ).replace(":", "-")
@@ -134,7 +144,7 @@ class BuilderBase:
134
144
  builds["output"] = {
135
145
  # type=oci cannot be loaded by docker: https://github.com/docker/buildx/issues/59
136
146
  "type": "docker",
137
- "dest": build_result.tarball_filenaem,
147
+ "dest": build_result.tarball_filename,
138
148
  }
139
149
  else:
140
150
  build_result.succeeded = False
@@ -155,21 +165,24 @@ class BuilderBase:
155
165
  f"Building Holoscan Application Package: tag={platform_parameters.tag}"
156
166
  )
157
167
 
168
+ self.print_build_info(platform_parameters)
169
+
158
170
  try:
159
171
  build_docker_image(**builds)
160
172
  build_result.succeeded = True
161
173
  if export_to_tar_ball:
162
174
  try:
163
175
  self._logger.info(
164
- f"Saving {platform_parameters.tag} to {build_result.tarball_filenaem}..."
176
+ f"Saving {platform_parameters.tag} to {build_result.tarball_filename}..."
165
177
  )
166
178
  docker_export_tarball(
167
- build_result.tarball_filenaem, platform_parameters.tag
179
+ build_result.tarball_filename, platform_parameters.tag
168
180
  )
169
181
  except Exception as ex:
170
182
  build_result.error = f"Error saving tarball: {ex}"
171
183
  build_result.succeeded = False
172
- except Exception:
184
+ except Exception as e:
185
+ print(e)
173
186
  build_result.succeeded = False
174
187
  build_result.error = (
175
188
  "Error building image: see Docker output for additional details."
@@ -177,6 +190,13 @@ class BuilderBase:
177
190
 
178
191
  return build_result
179
192
 
193
+ def _copy_input_data(self):
194
+ """Copy input data to temporary location"""
195
+ if self._build_parameters.input_data is not None:
196
+ shutil.copytree(
197
+ self._build_parameters.input_data, os.path.join(self._temp_dir, "input")
198
+ )
199
+
180
200
  def print_build_info(self, platform_parameters):
181
201
  """Print build information for the platform."""
182
202
  self._logger.info(
@@ -186,6 +206,7 @@ Building image for: {platform_parameters.platform.value}
186
206
  Architecture: {platform_parameters.platform_arch.value}
187
207
  Base Image: {platform_parameters.base_image}
188
208
  Build Image: {platform_parameters.build_image if platform_parameters.build_image is not None else "N/A"}
209
+ CUDA Version: {platform_parameters.cuda_version}
189
210
  Cache: {'Disabled' if self._build_parameters.no_cache else 'Enabled'}
190
211
  Configuration: {platform_parameters.platform_config.value}
191
212
  Holoscan SDK Package: {platform_parameters.holoscan_sdk_file if platform_parameters.holoscan_sdk_file is not None else "N/A"}
@@ -337,7 +358,15 @@ Building image for: {platform_parameters.platform.value}
337
358
  """
338
359
  )
339
360
 
340
- jinja_template = jinja_env.get_template("Dockerfile.jinja2")
361
+ if platform_parameters.cuda_version == 12:
362
+ jinja_template = jinja_env.get_template("Dockerfile-cu12.jinja2")
363
+ elif platform_parameters.cuda_version == 13:
364
+ jinja_template = jinja_env.get_template("Dockerfile.jinja2")
365
+ else:
366
+ raise IncompatiblePlatformConfigurationError(
367
+ f"Invalid CUDA version: {platform_parameters.cuda_version}"
368
+ )
369
+
341
370
  return jinja_template.render(
342
371
  {
343
372
  **self._build_parameters.to_jinja,
@@ -377,18 +406,28 @@ class PythonAppBuilder(BuilderBase):
377
406
  pip_folder = os.path.join(self._temp_dir, "pip")
378
407
  os.makedirs(pip_folder, exist_ok=True)
379
408
  pip_requirements_path = os.path.join(pip_folder, "requirements.txt")
409
+
410
+ # Build requirements content first
411
+ requirements_content = []
412
+ if self._build_parameters.requirements_file_path is not None:
413
+ with open(self._build_parameters.requirements_file_path) as lr:
414
+ requirements_content.extend(lr)
415
+ requirements_content.append("")
416
+
417
+ if self._build_parameters.pip_packages:
418
+ requirements_content.extend(self._build_parameters.pip_packages)
419
+
420
+ # Write all content at once
380
421
  with open(pip_requirements_path, "w") as requirements_file:
381
- # Use local requirements.txt packages if provided, otherwise use sdk provided packages
382
- if self._build_parameters.requirements_file_path is not None:
383
- with open(self._build_parameters.requirements_file_path) as lr:
384
- for line in lr:
385
- requirements_file.write(line)
386
- requirements_file.writelines("\n")
387
-
388
- if self._build_parameters.pip_packages:
389
- requirements_file.writelines(
390
- "\n".join(self._build_parameters.pip_packages)
391
- )
422
+ requirements_file.writelines(requirements_content)
423
+ self._logger.debug(
424
+ "================ Begin requirements.txt ================"
425
+ )
426
+ for req in requirements_content:
427
+ self._logger.debug(f" {req.strip()}")
428
+ self._logger.debug(
429
+ "================ End requirements.txt =================="
430
+ )
392
431
 
393
432
  def _copy_sdk_file(self, sdk_file: Optional[Path]):
394
433
  if sdk_file is not None and os.path.isfile(sdk_file):