@@ -264,6 +264,7 @@ async def download_files_async(
264264 os .path .expanduser (download_location )
265265 )
266266
267+ # 1. Fetch the server-generated manifest and read it into memory
267268 manifest_path = await DownloadList .get_manifest_async (
268269 synapse_client = client ,
269270 )
@@ -274,6 +275,7 @@ async def download_files_async(
274275 finally :
275276 os .remove (manifest_path )
276277
278+ # 2. Validate manifest columns
277279 if columns is None :
278280 raise SynapseError (
279281 "Manifest job succeeded but the downloaded CSV has no headers. "
@@ -289,6 +291,7 @@ async def download_files_async(
289291
290292 columns = list (columns ) + [_PATH_COLUMN , _ERROR_COLUMN ]
291293
294+ # 3. Download each file in the manifest
292295 downloaded_files = await DownloadList ._download_all_rows (
293296 rows = rows ,
294297 download_location = download_location ,
@@ -297,12 +300,14 @@ async def download_files_async(
297300 synapse_client = client ,
298301 )
299302
303+ # 4. Write the result manifest with path/error columns
300304 new_manifest_path = await DownloadList ._save_result_manifest (
301305 rows = rows ,
302306 columns = columns ,
303307 download_location = download_location ,
304308 )
305309
310+ # 5. Remove successfully downloaded files from the cart
306311 if downloaded_files :
307312 await remove_from_download_list_async (
308313 files = downloaded_files ,
@@ -337,63 +342,6 @@ def _read_manifest_rows(
337342 return None , []
338343 return list (columns ), rows
339344
340- @staticmethod
341- async def _download_row (
342- row : dict [str , Any ],
343- download_location : Optional [str ] = None ,
344- * ,
345- synapse_client : Optional ["Synapse" ] = None ,
346- ) -> Optional [DownloadListItem ]:
347- """Download the file described by a manifest row and record the result in place.
348-
349- On success, sets row["path"] to the local file path and row["error"]
350- to "". On failure, sets row["path"] to "" and row["error"] to the
351- error message. Failures are logged but never raised, so one bad file
352- does not abort the entire batch.
353-
354- Arguments:
355- row: A manifest row dict. Must contain "ID" and "versionNumber"
356- keys. Modified in place to add "path" and "error" entries.
357- download_location: Directory to download the file to. Defaults to
358- the Synapse cache location if None.
359- synapse_client: Optional Synapse client. Uses cached singleton if omitted.
360-
361- Returns:
362- A DownloadListItem on success, or None on failure.
363- """
364- from synapseclient import Synapse
365- from synapseclient .models .file import File
366-
367- client = Synapse .get_client (synapse_client = synapse_client )
368- entity_id = row ["ID" ]
369- version_str = row .get ("versionNumber" )
370- version_number = int (version_str ) if version_str else None
371-
372- if version_number is None :
373- msg = f"Manifest row for { entity_id } is missing versionNumber"
374- row [_PATH_COLUMN ] = ""
375- row [_ERROR_COLUMN ] = msg
376- client .logger .warning (msg )
377- return None
378-
379- try :
380- file = await File (
381- id = entity_id ,
382- version_number = version_number ,
383- path = download_location ,
384- ).get_async (synapse_client = client )
385- row [_PATH_COLUMN ] = file .path or ""
386- row [_ERROR_COLUMN ] = ""
387- return DownloadListItem (
388- file_entity_id = entity_id ,
389- version_number = version_number ,
390- )
391- except Exception as e :
392- row [_PATH_COLUMN ] = ""
393- row [_ERROR_COLUMN ] = str (e )
394- client .logger .exception (f"Unable to download { entity_id } v{ version_number } " )
395- return None
396-
397345 @staticmethod
398346 def _write_result_manifest (
399347 path : str ,
@@ -484,6 +432,63 @@ async def bounded_download(
484432 downloaded .append (item )
485433 return downloaded
486434
435+ @staticmethod
436+ async def _download_row (
437+ row : dict [str , Any ],
438+ download_location : Optional [str ] = None ,
439+ * ,
440+ synapse_client : Optional ["Synapse" ] = None ,
441+ ) -> Optional [DownloadListItem ]:
442+ """Download the file described by a manifest row and record the result in place.
443+
444+ On success, sets row["path"] to the local file path and row["error"]
445+ to "". On failure, sets row["path"] to "" and row["error"] to the
446+ error message. Failures are logged but never raised, so one bad file
447+ does not abort the entire batch.
448+
449+ Arguments:
450+ row: A manifest row dict. Must contain "ID" and "versionNumber"
451+ keys. Modified in place to add "path" and "error" entries.
452+ download_location: Directory to download the file to. Defaults to
453+ the Synapse cache location if None.
454+ synapse_client: Optional Synapse client. Uses cached singleton if omitted.
455+
456+ Returns:
457+ A DownloadListItem on success, or None on failure.
458+ """
459+ from synapseclient import Synapse
460+ from synapseclient .models .file import File
461+
462+ client = Synapse .get_client (synapse_client = synapse_client )
463+ entity_id = row ["ID" ]
464+ version_str = row .get ("versionNumber" )
465+ version_number = int (version_str ) if version_str else None
466+
467+ if version_number is None :
468+ msg = f"Manifest row for { entity_id } is missing versionNumber"
469+ row [_PATH_COLUMN ] = ""
470+ row [_ERROR_COLUMN ] = msg
471+ client .logger .warning (msg )
472+ return None
473+
474+ try :
475+ file = await File (
476+ id = entity_id ,
477+ version_number = version_number ,
478+ path = download_location ,
479+ ).get_async (synapse_client = client )
480+ row [_PATH_COLUMN ] = file .path or ""
481+ row [_ERROR_COLUMN ] = ""
482+ return DownloadListItem (
483+ file_entity_id = entity_id ,
484+ version_number = version_number ,
485+ )
486+ except Exception as e :
487+ row [_PATH_COLUMN ] = ""
488+ row [_ERROR_COLUMN ] = str (e )
489+ client .logger .exception (f"Unable to download { entity_id } v{ version_number } " )
490+ return None
491+
487492 @staticmethod
488493 async def _save_result_manifest (
489494 rows : list [dict [str , Any ]],
0 commit comments