truefoundry 0.11.10__py3-none-any.whl → 0.11.11__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 truefoundry might be problematic. Click here for more details.

@@ -22,6 +22,23 @@ from truefoundry.pydantic_v1 import BaseModel
22
22
  logger = logging.getLogger("truefoundry")
23
23
 
24
24
 
25
+ def truncate_path_for_progress(
26
+ path: str, max_length: int, relpath: bool = False
27
+ ) -> str:
28
+ if relpath:
29
+ path = os.path.relpath(path)
30
+ if len(path) <= max_length:
31
+ return path
32
+ parts = path.split(os.sep)
33
+ result = parts[-1] # start with filename
34
+ i = len(parts) - 2
35
+ # keep prepending directories until adding more would exceed max_len - 3
36
+ while i >= 0 and len(result) + len(parts[i]) + 1 <= max_length - 3:
37
+ result = parts[i] + os.sep + result
38
+ i -= 1
39
+ return "..." + os.sep + result
40
+
41
+
25
42
  class MultiPartUploadStorageProvider(str, Enum):
26
43
  S3_COMPATIBLE = "S3_COMPATIBLE"
27
44
  AZURE_BLOB = "AZURE_BLOB"
@@ -182,7 +199,7 @@ def _file_part_upload(
182
199
  return response
183
200
 
184
201
 
185
- def s3_compatible_multipart_upload(
202
+ def s3_compatible_multipart_upload( # noqa: C901
186
203
  multipart_upload: MultiPartUpload,
187
204
  local_file: str,
188
205
  multipart_info: _FileMultiPartInfo,
@@ -190,13 +207,15 @@ def s3_compatible_multipart_upload(
190
207
  progress_bar: Optional[Progress] = None,
191
208
  abort_event: Optional[Event] = None,
192
209
  exception_class=HttpRequestException,
193
- ):
210
+ ) -> None:
194
211
  abort_event = abort_event or Event()
195
212
  parts = []
196
213
 
197
- if progress_bar:
214
+ if progress_bar is not None:
198
215
  multi_part_upload_progress = progress_bar.add_task(
199
- f"[green]Uploading {local_file}:", start=True
216
+ f"[green] {truncate_path_for_progress(local_file, 64, relpath=True)}",
217
+ start=True,
218
+ visible=True,
200
219
  )
201
220
 
202
221
  def upload(part_number: int, seek: int) -> None:
@@ -221,7 +240,7 @@ def s3_compatible_multipart_upload(
221
240
  multipart_info.num_parts,
222
241
  local_file,
223
242
  )
224
- if progress_bar:
243
+ if progress_bar is not None:
225
244
  progress_bar.update(
226
245
  multi_part_upload_progress,
227
246
  advance=multipart_info.part_size,
@@ -254,10 +273,12 @@ def s3_compatible_multipart_upload(
254
273
  timeout=2 * 60,
255
274
  )
256
275
  response.raise_for_status()
276
+ if progress_bar is not None:
277
+ progress_bar.refresh()
257
278
  logger.debug("Multipart upload of %s completed", local_file)
258
279
 
259
280
 
260
- def azure_multi_part_upload(
281
+ def azure_multi_part_upload( # noqa: C901
261
282
  multipart_upload: MultiPartUpload,
262
283
  local_file: str,
263
284
  multipart_info: _FileMultiPartInfo,
@@ -265,12 +286,14 @@ def azure_multi_part_upload(
265
286
  progress_bar: Optional[Progress] = None,
266
287
  abort_event: Optional[Event] = None,
267
288
  exception_class=HttpRequestException,
268
- ):
289
+ ) -> None:
269
290
  abort_event = abort_event or Event()
270
291
 
271
- if progress_bar:
292
+ if progress_bar is not None:
272
293
  multi_part_upload_progress = progress_bar.add_task(
273
- f"[green]Uploading {local_file}:", start=True
294
+ f"[green] {truncate_path_for_progress(local_file, 64, relpath=True)}",
295
+ start=True,
296
+ visible=True,
274
297
  )
275
298
 
276
299
  def upload(part_number: int, seek: int):
@@ -289,7 +312,7 @@ def azure_multi_part_upload(
289
312
  abort_event=abort_event,
290
313
  exception_class=exception_class,
291
314
  )
292
- if progress_bar:
315
+ if progress_bar is not None:
293
316
  progress_bar.update(
294
317
  multi_part_upload_progress,
295
318
  advance=multipart_info.part_size,
@@ -328,4 +351,6 @@ def azure_multi_part_upload(
328
351
  timeout=2 * 60,
329
352
  )
330
353
  response.raise_for_status()
354
+ if progress_bar is not None:
355
+ progress_bar.refresh()
331
356
  logger.debug("Multipart upload of %s completed", local_file)
@@ -40,6 +40,7 @@ from truefoundry.common.storage_provider_utils import (
40
40
  azure_multi_part_upload,
41
41
  decide_file_parts,
42
42
  s3_compatible_multipart_upload,
43
+ truncate_path_for_progress,
43
44
  )
44
45
  from truefoundry.ml._autogen.client import ( # type: ignore[attr-defined]
45
46
  ApiClient,
@@ -125,14 +126,14 @@ def _signed_url_upload_file(
125
126
  augmented_raise_for_status(response, exception_class=MlFoundryException) # type: ignore
126
127
  return
127
128
 
128
- task_progress_bar = progress_bar.add_task(
129
- f"[green]Uploading {local_file}:", start=True
129
+ task_id = progress_bar.add_task(
130
+ f"[green] {truncate_path_for_progress(local_file, 64, relpath=True)}",
131
+ start=True,
132
+ visible=True,
130
133
  )
131
134
 
132
135
  def callback(length):
133
- progress_bar.update(
134
- task_progress_bar, advance=length, total=os.stat(local_file).st_size
135
- )
136
+ progress_bar.update(task_id, advance=length, total=os.stat(local_file).st_size)
136
137
  if abort_event and abort_event.is_set():
137
138
  raise Exception("aborting upload")
138
139
 
@@ -147,6 +148,9 @@ def _signed_url_upload_file(
147
148
  ) as response:
148
149
  augmented_raise_for_status(response, exception_class=MlFoundryException) # type: ignore
149
150
 
151
+ if progress_bar is not None:
152
+ progress_bar.refresh()
153
+
150
154
 
151
155
  def _download_file_using_http_uri(
152
156
  http_uri,
@@ -167,11 +171,18 @@ def _download_file_using_http_uri(
167
171
  exception_class=MlFoundryException, # type: ignore
168
172
  ) as response:
169
173
  augmented_raise_for_status(response, exception_class=MlFoundryException) # type: ignore
170
- file_size = int(response.headers.get("Content-Length", 0))
174
+ file_size = int(response.headers.get("Content-Length", -1))
175
+ if file_size == 0 and callback:
176
+ # special case for empty files
177
+ callback(1, 1)
178
+ file_size = file_size if file_size > 0 else 0
171
179
  with open(download_path, "wb") as output_file:
172
180
  for chunk in response.iter_content(chunk_size=chunk_size):
173
181
  if callback:
174
182
  callback(len(chunk), file_size)
183
+ print(
184
+ f"Sent callback for {download_path} {len(chunk)} / {file_size}"
185
+ )
175
186
  if not chunk:
176
187
  break
177
188
  output_file.write(chunk)
@@ -658,15 +669,18 @@ class MlFoundryArtifactsRepository:
658
669
  logger.info("Downloading %s to %s", remote_file_path, local_path)
659
670
 
660
671
  if progress_bar is not None:
661
- download_progress_bar = progress_bar.add_task(
662
- f"[green]Downloading to {remote_file_path}:", start=True
672
+ task_id = progress_bar.add_task(
673
+ f"[green] {truncate_path_for_progress(remote_file_path, 64)}",
674
+ start=True,
675
+ visible=True,
663
676
  )
664
677
 
665
- def callback(chunk, total_file_size):
678
+ def callback(chunk_size: int, total_file_size: int):
679
+ nonlocal task_id
666
680
  if progress_bar is not None:
667
681
  progress_bar.update(
668
- download_progress_bar,
669
- advance=chunk,
682
+ task_id,
683
+ advance=chunk_size,
670
684
  total=total_file_size,
671
685
  )
672
686
  if abort_event and abort_event.is_set():
@@ -677,6 +691,9 @@ class MlFoundryArtifactsRepository:
677
691
  download_path=local_path,
678
692
  callback=callback,
679
693
  )
694
+
695
+ if progress_bar is not None:
696
+ progress_bar.refresh()
680
697
  logger.debug("Downloaded %s to %s", remote_file_path, local_path)
681
698
 
682
699
  def _download_artifact(
@@ -347,10 +347,11 @@ class ArtifactVersion:
347
347
  artifact_version.download(path="<your-desired-download-path>")
348
348
  ```
349
349
  """
350
- download_path = self._download(
350
+ download_info = self._download(
351
351
  path=path, overwrite=overwrite, progress=progress
352
352
  )
353
- return download_path.download_dir
353
+ logger.info("Downloaded artifact contents to %s", download_info.download_dir)
354
+ return download_info.download_dir
354
355
 
355
356
  def delete(self) -> bool:
356
357
  """
@@ -440,6 +440,7 @@ class ModelVersion:
440
440
  download_info = self._download(
441
441
  path=path, overwrite=overwrite, progress=progress
442
442
  )
443
+ logger.info("Downloaded model contents to %s", download_info.model_dir)
443
444
  return download_info
444
445
 
445
446
  def delete(self) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: truefoundry
3
- Version: 0.11.10
3
+ Version: 0.11.11
4
4
  Summary: TrueFoundry CLI
5
5
  Author-email: TrueFoundry Team <abhishek@truefoundry.com>
6
6
  Requires-Python: <3.14,>=3.8.1
@@ -48,7 +48,7 @@ truefoundry/common/exceptions.py,sha256=jkU0N7hV_P-EhXeud4I5vuB9glXXZSWPf8LcH04m
48
48
  truefoundry/common/request_utils.py,sha256=e9qrAQ1MutU7JALDKcucmNd0KQEVBqgW3yx0w1zeHIU,5700
49
49
  truefoundry/common/servicefoundry_client.py,sha256=2fYhdVPSvLXz5C5tosOq86JD8WM3IRUIy1VO9deDxZI,3340
50
50
  truefoundry/common/session.py,sha256=d9l3TEBpqVP4mr4mTGY1qVxc815skzMlNNdw14otg34,2923
51
- truefoundry/common/storage_provider_utils.py,sha256=yURhMw8k0FLFvaviRHDiifhvc6GnuQwGMC9Qd2uM440,10934
51
+ truefoundry/common/storage_provider_utils.py,sha256=jX9maCtWusZx63324cmPxHiGeIluZZzbDaJ1QYTIhAw,11856
52
52
  truefoundry/common/types.py,sha256=BMJFCsR1lPJAw66IQBSvLyV4I6o_x5oj78gVsUa9si8,188
53
53
  truefoundry/common/utils.py,sha256=P0FuAadoJGdpieUORLSN-PiFnkyoGO-K2cS4OPITBWg,6714
54
54
  truefoundry/common/warnings.py,sha256=xDMhR_-ZGC40Ycaj6nlFb5MYPexn8WbKCHd4FlflTXQ,705
@@ -349,7 +349,7 @@ truefoundry/ml/_autogen/models/schema.py,sha256=a_bp42MMPUbwO3407m0UW2W8EOhnxZXf
349
349
  truefoundry/ml/_autogen/models/signature.py,sha256=rBjpxUIsEeWM0sIyYG5uCJB18DKHR4k5yZw8TzuoP48,4987
350
350
  truefoundry/ml/_autogen/models/utils.py,sha256=c7RtSLXhOLcP8rjuUtfnMdaKVTZvvbsmw98gPAkAFrs,24371
351
351
  truefoundry/ml/artifact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
352
- truefoundry/ml/artifact/truefoundry_artifact_repo.py,sha256=qZ8p3IZYBiyMQjhMt_ZM46b6O_gHMy0ZUFKU-EylS3o,35685
352
+ truefoundry/ml/artifact/truefoundry_artifact_repo.py,sha256=ITZbhvzwa8NbN66K7V9iufzZHVguuLYn1pHCDSE-P-o,36273
353
353
  truefoundry/ml/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
354
354
  truefoundry/ml/cli/cli.py,sha256=MwpY7z_NEeJE_XIP7XbZELjNeu2vpMmohttHCKDRk54,335
355
355
  truefoundry/ml/cli/utils.py,sha256=j6_mZ4Spn114mz3P4QQ8jx0tmorXIuyQnHXVUSDvZi4,1035
@@ -365,11 +365,11 @@ truefoundry/ml/log_types/plot.py,sha256=LDh4uy6z2P_a2oPM2lc85c0lt8utVvunohzeMawF
365
365
  truefoundry/ml/log_types/pydantic_base.py,sha256=eBlw_AEyAz4iJKDP4zgJOCFWcldwQqpf7FADW1jzIQY,272
366
366
  truefoundry/ml/log_types/utils.py,sha256=xjJ21jdPScvFmw3TbVh5NCzbzJwaqiXJyiiT4xxX1EI,335
367
367
  truefoundry/ml/log_types/artifacts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
368
- truefoundry/ml/log_types/artifacts/artifact.py,sha256=JiqdWp6XQe2S_zP96urXCjyfdxFlx6-GNBzH9Te7hTQ,19854
368
+ truefoundry/ml/log_types/artifacts/artifact.py,sha256=OXJDaJZLGE8jA-6A8pdApfpbz1x3O-8rIMWUkG_8hYA,19940
369
369
  truefoundry/ml/log_types/artifacts/constants.py,sha256=7blG13UXSkZVZkN8EDFqaH-a_ZonJA6pI0gwJwkIZ-s,1298
370
370
  truefoundry/ml/log_types/artifacts/dataset.py,sha256=OgWIoT59AhMw8P01FfvUKbJ3EL6HQf_Xw8X4E3Ff5Sg,13172
371
371
  truefoundry/ml/log_types/artifacts/general_artifact.py,sha256=yr-SQ2fhUR_sE1MB5zoHHYpGC8tizH_-t3lhsxCAULU,2747
372
- truefoundry/ml/log_types/artifacts/model.py,sha256=oRX6bKb5tN4LjDFifeDpjv4z1-vHXf3Ma6-nkp7mN4M,24831
372
+ truefoundry/ml/log_types/artifacts/model.py,sha256=j4Gf0TKpi-JCOBqCdkH40pkpDLwvSk04KgQn2sK5fZI,24911
373
373
  truefoundry/ml/log_types/artifacts/utils.py,sha256=INZhhzl6OaD6qFAyxaLJAhXhFd6wKNujMj-lq2v8p4Q,9340
374
374
  truefoundry/ml/log_types/image/__init__.py,sha256=fcOq8yQnNj1rkLcPeIjLXBpdA1WIeiPsXOlAAvMxx7M,76
375
375
  truefoundry/ml/log_types/image/constants.py,sha256=wLtGEOA4T5fZHSlOXPuNDLX3lpbCtwlvGKPFk_1fah0,255
@@ -387,7 +387,7 @@ truefoundry/workflow/remote_filesystem/__init__.py,sha256=LQ95ViEjJ7Ts4JcCGOxMPs
387
387
  truefoundry/workflow/remote_filesystem/logger.py,sha256=em2l7D6sw7xTLDP0kQSLpgfRRCLpN14Qw85TN7ujQcE,1022
388
388
  truefoundry/workflow/remote_filesystem/tfy_signed_url_client.py,sha256=xcT0wQmQlgzcj0nP3tJopyFSVWT1uv3nhiTIuwfXYeg,12342
389
389
  truefoundry/workflow/remote_filesystem/tfy_signed_url_fs.py,sha256=nSGPZu0Gyd_jz0KsEE-7w_BmnTD8CVF1S8cUJoxaCbc,13305
390
- truefoundry-0.11.10.dist-info/METADATA,sha256=ipgkIpx7Ho9NogRqOTZcBiOCnAMUE-BbOBHhvxE7i50,2761
391
- truefoundry-0.11.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
- truefoundry-0.11.10.dist-info/entry_points.txt,sha256=xVjn7RMN-MW2-9f7YU-bBdlZSvvrwzhpX1zmmRmsNPU,98
393
- truefoundry-0.11.10.dist-info/RECORD,,
390
+ truefoundry-0.11.11.dist-info/METADATA,sha256=KvW4uvzJ44zwy8dOpOK_NfMp3IS2h3Z44gG5OOowTHI,2761
391
+ truefoundry-0.11.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
+ truefoundry-0.11.11.dist-info/entry_points.txt,sha256=xVjn7RMN-MW2-9f7YU-bBdlZSvvrwzhpX1zmmRmsNPU,98
393
+ truefoundry-0.11.11.dist-info/RECORD,,