@@ -268,7 +268,8 @@ def download(
268268 Will automatically generate any necessary directories if they do not
269269 already exist.
270270
271- Makes one or many `GET /batch/download/{job_id}/{filename}` HTTP request(s).
271+ Makes a `GET /batch.download` HTTP request for full job downloads,
272+ or `GET /batch/download/{job_id}/{filename}` for individual files.
272273
273274 Parameters
274275 ----------
@@ -303,20 +304,28 @@ def download(
303304 "Cannot specify an individual file to download when `keep_zip=True`" ,
304305 )
305306
306- batch_download = _BatchJob (
307- self ,
308- job_id = job_id ,
309- output_dir = output_dir ,
310- )
307+ if output_dir is None :
308+ output_dir = Path .cwd ()
309+ else :
310+ output_dir = validate_path (output_dir , "output_dir" )
311+
312+ job_output_dir = output_dir / job_id
313+ job_output_dir .mkdir (exist_ok = True , parents = True )
311314
312315 if filename_to_download is None :
313- filenames_to_download = None
316+ # Download all files as ZIP using batch.download endpoint
317+ zip_path = job_output_dir / f"{ job_id } .zip"
318+ downloaded_files = [self ._download_batch_zip (job_id , zip_path )]
314319 else :
315- filenames_to_download = [filename_to_download ]
316-
317- downloaded_files = batch_download .download (
318- filenames_to_download = filenames_to_download ,
319- )
320+ # Download specific file using _BatchJob
321+ batch_download = _BatchJob (
322+ self ,
323+ job_id = job_id ,
324+ output_dir = output_dir ,
325+ )
326+ downloaded_files = batch_download .download (
327+ filenames_to_download = [filename_to_download ],
328+ )
320329
321330 if keep_zip :
322331 return downloaded_files
@@ -348,7 +357,8 @@ async def download_async(
348357 Will automatically generate any necessary directories if they do not
349358 already exist.
350359
351- Makes one or many `GET /batch/download/{job_id}/{filename}` HTTP request(s).
360+ Makes a `GET /batch.download` HTTP request for full job downloads,
361+ or `GET /batch/download/{job_id}/{filename}` for individual files.
352362
353363 Parameters
354364 ----------
@@ -382,20 +392,31 @@ async def download_async(
382392 "Cannot specify an individual file to download when `keep_zip=True`" ,
383393 )
384394
385- batch_download = _BatchJob (
386- self ,
387- job_id = job_id ,
388- output_dir = output_dir ,
389- )
395+ output_dir = validate_path (output_dir , "output_dir" )
396+ job_output_dir = output_dir / job_id
397+ job_output_dir .mkdir (exist_ok = True , parents = True )
390398
391399 if filename_to_download is None :
392- filenames_to_download = None
400+ # Download all files as ZIP using batch.download endpoint
401+ zip_path = job_output_dir / f"{ job_id } .zip"
402+ downloaded_files = [
403+ await asyncio .get_running_loop ().run_in_executor (
404+ _BatchJob ._executor ,
405+ self ._download_batch_zip ,
406+ job_id ,
407+ zip_path ,
408+ ),
409+ ]
393410 else :
394- filenames_to_download = [filename_to_download ]
395-
396- downloaded_files = await batch_download .download_async (
397- filenames_to_download = filenames_to_download ,
398- )
411+ # Download specific file using _BatchJob
412+ batch_download = _BatchJob (
413+ self ,
414+ job_id = job_id ,
415+ output_dir = output_dir ,
416+ )
417+ downloaded_files = await batch_download .download_async (
418+ filenames_to_download = [filename_to_download ],
419+ )
399420
400421 if keep_zip :
401422 return downloaded_files
@@ -520,16 +541,16 @@ def _download_batch_file(
520541
521542 def _download_batch_zip (
522543 self ,
523- batch_download_url : str ,
544+ job_id : str ,
524545 output_path : Path ,
525546 ) -> Path :
526547 """
527548 Download all batch files as a .zip archive.
528549
529550 Parameters
530551 ----------
531- batch_download_url : _BatchDownloadFile
532- The batch download URL for the zipfile, should contain the job ID appended with .zip .
552+ job_id : str
553+ The job ID of the batch job to download .
533554 output_path : Path
534555 The output path of the file.
535556
@@ -553,7 +574,8 @@ def _download_batch_zip(
553574
554575 try :
555576 with requests .get (
556- url = batch_download_url ,
577+ url = f"{ self ._base_url } .download" ,
578+ params = {"job_id" : job_id },
557579 headers = headers ,
558580 auth = HTTPBasicAuth (username = self ._key , password = "" ),
559581 allow_redirects = True ,
@@ -586,7 +608,7 @@ def _download_batch_zip(
586608
587609class _BatchJob :
588610 """
589- Helper class for downloading multiple batch files from a job.
611+ Helper class for downloading individual batch files from a job.
590612
591613 Supports sync and async downloads using a shared
592614 `ThreadPoolExecutor`.
@@ -619,9 +641,6 @@ def __init__(
619641 logger .error (error_message )
620642 raise RuntimeError (error_message )
621643
622- zip_filename = f"{ job_id } .zip"
623- zip_url = None
624-
625644 batch_files = []
626645 for file_detail in job_details :
627646 try :
@@ -645,9 +664,6 @@ def __init__(
645664 "'download' delivery is not available for this job." ,
646665 ) from None
647666
648- if zip_url is None :
649- zip_url = urls ["https" ].replace (filename , zip_filename )
650-
651667 batch_files .append (
652668 _BatchJob ._BatchJobFile (
653669 filename = filename ,
@@ -657,18 +673,16 @@ def __init__(
657673 ),
658674 )
659675
660- if not batch_files or not zip_url :
676+ if not batch_files :
661677 raise ValueError (f"No job files for { job_id } ." )
662678
663- self ._zip_filename = zip_filename
664- self ._zip_url = zip_url
665679 self ._batch_http_api = batch_http_api
666680 self ._output_dir = validate_path (output_dir , "output_dir" ) / job_id
667681 self ._batch_files = batch_files
668682
669683 def download (
670684 self ,
671- filenames_to_download : Iterable [str ] | None ,
685+ filenames_to_download : Iterable [str ],
672686 ) -> list [Path ]:
673687 self ._output_dir .mkdir (
674688 exist_ok = True ,
@@ -678,31 +692,22 @@ def download(
678692 file_paths = []
679693 tasks = []
680694
681- if filenames_to_download is None :
695+ filenames_to_download = set (filenames_to_download )
696+ for batch_file in self ._batch_files :
697+ if not filenames_to_download :
698+ break
699+
700+ if batch_file .filename not in filenames_to_download :
701+ continue
702+
682703 tasks .append (
683704 self ._executor .submit (
684- self ._batch_http_api ._download_batch_zip ,
685- self . _zip_url ,
686- self ._output_dir / self . _zip_filename ,
705+ self ._batch_http_api ._download_batch_file ,
706+ batch_file ,
707+ self ._output_dir / batch_file . filename ,
687708 ),
688709 )
689- else :
690- filenames_to_download = set (filenames_to_download )
691- for batch_file in self ._batch_files :
692- if not filenames_to_download :
693- break
694-
695- if batch_file .filename not in filenames_to_download :
696- continue
697-
698- tasks .append (
699- self ._executor .submit (
700- self ._batch_http_api ._download_batch_file ,
701- batch_file ,
702- self ._output_dir / batch_file .filename ,
703- ),
704- )
705- filenames_to_download .remove (batch_file .filename )
710+ filenames_to_download .remove (batch_file .filename )
706711
707712 for completed in as_completed (tasks ):
708713 path = completed .result ()
@@ -712,7 +717,7 @@ def download(
712717
713718 async def download_async (
714719 self ,
715- filenames_to_download : Iterable [str ] | None ,
720+ filenames_to_download : Iterable [str ],
716721 ) -> list [Path ]:
717722 self ._output_dir .mkdir (
718723 exist_ok = True ,
@@ -722,34 +727,22 @@ async def download_async(
722727 file_paths : list [Path ] = []
723728 tasks = []
724729
725- if filenames_to_download is None :
730+ filenames_to_download = set (filenames_to_download )
731+ for batch_file in self ._batch_files :
732+ if not filenames_to_download :
733+ break
734+
735+ if batch_file .filename not in filenames_to_download :
736+ continue
737+
726738 tasks .append (
727739 asyncio .get_running_loop ().run_in_executor (
728740 self ._executor ,
729- self ._batch_http_api ._download_batch_zip ,
730- self . _zip_url ,
731- self ._output_dir / self . _zip_filename ,
741+ self ._batch_http_api ._download_batch_file ,
742+ batch_file ,
743+ self ._output_dir / batch_file . filename ,
732744 ),
733745 )
734- else :
735- filenames_to_download = set (filenames_to_download )
736-
737- tasks = []
738- for batch_file in self ._batch_files :
739- if not filenames_to_download :
740- break
741-
742- if batch_file .filename not in filenames_to_download :
743- continue
744-
745- tasks .append (
746- asyncio .get_running_loop ().run_in_executor (
747- self ._executor ,
748- self ._batch_http_api ._download_batch_file ,
749- batch_file ,
750- self ._output_dir / batch_file .filename ,
751- ),
752- )
753746
754747 for completed in asyncio .as_completed (tasks ):
755748 try :
0 commit comments