3333from .._base_client import _BaseClient , _RawResponse , _StreamingResponse
3434from .._property import _cached_property
3535from ..config import Config
36- from ..errors import AlreadyExists , NotFound , PermissionDenied
36+ from ..errors import AlreadyExists , InternalError , NotFound , PermissionDenied
3737from ..errors .mapper import _error_mapper
3838from ..retries import retried
3939from ..service import files
@@ -1064,7 +1064,7 @@ def _get_optimized_performance_parameters_for_upload(
10641064 def upload (
10651065 self ,
10661066 file_path : str ,
1067- content : BinaryIO ,
1067+ contents : BinaryIO ,
10681068 * ,
10691069 overwrite : Optional [bool ] = None ,
10701070 part_size : Optional [int ] = None ,
@@ -1076,7 +1076,7 @@ def upload(
10761076
10771077 :param file_path: str
10781078 The absolute remote path of the target file, e.g. /Volumes/path/to/your/file
1079- :param content : BinaryIO
1079+ :param contents : BinaryIO
10801080 The contents of the file to upload. This must be a BinaryIO stream.
10811081 :param overwrite: bool (optional)
10821082 If true, an existing file will be overwritten. When not specified, assumed True.
@@ -1096,7 +1096,7 @@ def upload(
10961096
10971097 if self ._config .disable_experimental_files_api_client :
10981098 _LOG .info ("Disable experimental files API client, will use the original upload method." )
1099- super ().upload (file_path = file_path , contents = content , overwrite = overwrite )
1099+ super ().upload (file_path = file_path , contents = contents , overwrite = overwrite )
11001100 return UploadStreamResult ()
11011101
11021102 _LOG .debug (f"Uploading file from BinaryIO stream" )
@@ -1107,12 +1107,12 @@ def upload(
11071107
11081108 # Determine content length if the stream is seekable
11091109 content_length = None
1110- if content .seekable ():
1110+ if contents .seekable ():
11111111 _LOG .debug (f"Uploading using seekable mode" )
11121112 # If the stream is seekable, we can read its size.
1113- content .seek (0 , os .SEEK_END )
1114- content_length = content .tell ()
1115- content .seek (0 )
1113+ contents .seek (0 , os .SEEK_END )
1114+ content_length = contents .tell ()
1115+ contents .seek (0 )
11161116
11171117 # Get optimized part size and batch size based on content length and provided part size
11181118 optimized_part_size , optimized_batch_size = self ._get_optimized_performance_parameters_for_upload (
@@ -1134,18 +1134,20 @@ def upload(
11341134 f"Upload context: part_size={ ctx .part_size } , batch_size={ ctx .batch_size } , content_length={ ctx .content_length } "
11351135 )
11361136
1137- if ctx .use_parallel :
1138- self ._parallel_upload_from_stream (ctx , content )
1137+ if ctx .use_parallel and (
1138+ ctx .content_length is None or ctx .content_length >= self ._config .files_ext_multipart_upload_min_stream_size
1139+ ):
1140+ self ._parallel_upload_from_stream (ctx , contents )
11391141 return UploadStreamResult ()
11401142 elif ctx .content_length is not None :
1141- self ._upload_single_thread_with_known_size (ctx , content )
1143+ self ._upload_single_thread_with_known_size (ctx , contents )
11421144 return UploadStreamResult ()
11431145 else :
11441146 _LOG .debug (f"Uploading using non-seekable mode" )
11451147 # If the stream is not seekable, we cannot determine its size.
11461148 # We will use a multipart upload.
11471149 _LOG .debug (f"Using multipart upload for non-seekable input stream of unknown size for file { file_path } " )
1148- self ._single_thread_multipart_upload (ctx , content )
1150+ self ._single_thread_multipart_upload (ctx , contents )
11491151 return UploadStreamResult ()
11501152
11511153 def upload_from (
@@ -1206,7 +1208,7 @@ def upload_from(
12061208 use_parallel = use_parallel ,
12071209 parallelism = parallelism ,
12081210 )
1209- if ctx .use_parallel :
1211+ if ctx .use_parallel and ctx . content_length >= self . _config . files_ext_multipart_upload_min_stream_size :
12101212 self ._parallel_upload_from_file (ctx )
12111213 return UploadFileResult ()
12121214 else :
@@ -1459,8 +1461,9 @@ def _parallel_multipart_upload_from_stream(
14591461 # Do the first part read ahead
14601462 pre_read_buffer = content .read (ctx .part_size )
14611463 if not pre_read_buffer :
1462- self ._complete_multipart_upload (ctx , {}, session_token )
1463- return
1464+ raise FallbackToUploadUsingFilesApi (
1465+ b"" , "Falling back to single-shot upload with Files API due to empty input stream"
1466+ )
14641467 try :
14651468 etag = self ._do_upload_one_part (
14661469 ctx , cloud_provider_session , 1 , 0 , len (pre_read_buffer ), session_token , BytesIO (pre_read_buffer )
@@ -1650,6 +1653,13 @@ def _do_upload_one_part(
16501653 raise FallbackToUploadUsingFilesApi (None , "Presigned URLs are disabled" )
16511654 else :
16521655 raise e from None
1656+ except InternalError as e :
1657+ if self ._is_presigned_urls_network_zone_error (e ):
1658+ raise FallbackToUploadUsingFilesApi (
1659+ None , "Presigned URLs are not supported in the current network zone"
1660+ )
1661+ else :
1662+ raise e from None
16531663
16541664 upload_part_urls = upload_part_urls_response .get ("upload_part_urls" , [])
16551665 if len (upload_part_urls ) == 0 :
@@ -1760,6 +1770,13 @@ def _perform_multipart_upload(
17601770 raise FallbackToUploadUsingFilesApi (buffer , "Presigned URLs are disabled" )
17611771 else :
17621772 raise e from None
1773+ except InternalError as e :
1774+ if chunk_offset == 0 and self ._is_presigned_urls_network_zone_error (e ):
1775+ raise FallbackToUploadUsingFilesApi (
1776+ buffer , "Presigned URLs are not supported in the current network zone"
1777+ )
1778+ else :
1779+ raise e from None
17631780
17641781 upload_part_urls = upload_part_urls_response .get ("upload_part_urls" , [])
17651782 if len (upload_part_urls ) == 0 :
@@ -1917,6 +1934,13 @@ def _is_presigned_urls_disabled_error(self, e: PermissionDenied) -> bool:
19171934 return True
19181935 return False
19191936
1937+ def _is_presigned_urls_network_zone_error (self , e : InternalError ) -> bool :
1938+ error_infos = e .get_error_info ()
1939+ for error_info in error_infos :
1940+ if error_info .reason == "FILES_API_REQUESTER_NETWORK_ZONE_UNKNOWN" :
1941+ return True
1942+ return False
1943+
19201944 def _perform_resumable_upload (
19211945 self ,
19221946 ctx : _UploadContext ,
@@ -1966,6 +1990,13 @@ def _perform_resumable_upload(
19661990 raise FallbackToUploadUsingFilesApi (pre_read_buffer , "Presigned URLs are disabled" )
19671991 else :
19681992 raise e from None
1993+ except InternalError as e :
1994+ if self ._is_presigned_urls_network_zone_error (e ):
1995+ raise FallbackToUploadUsingFilesApi (
1996+ pre_read_buffer , "Presigned URLs are not supported in the current network zone"
1997+ )
1998+ else :
1999+ raise e from None
19692000
19702001 resumable_upload_url_node = resumable_upload_url_response .get ("resumable_upload_url" )
19712002 if not resumable_upload_url_node :
@@ -2350,6 +2381,11 @@ def _create_download_url(self, file_path: str) -> CreateDownloadUrlResponse:
23502381 raise FallbackToDownloadUsingFilesApi (f"Presigned URLs are disabled" )
23512382 else :
23522383 raise e from None
2384+ except InternalError as e :
2385+ if self ._is_presigned_urls_network_zone_error (e ):
2386+ raise FallbackToDownloadUsingFilesApi ("Presigned URLs are not supported in the current network zone" )
2387+ else :
2388+ raise e from None
23532389
23542390 def _init_download_response_presigned_api (self , file_path : str , added_headers : dict [str , str ]) -> DownloadResponse :
23552391 """
0 commit comments