@@ -192,13 +192,30 @@ def _transfer_config(self) -> TransferConfig:
192
192
@property
193
193
def client (self ) -> boto3 .client :
194
194
"""Client object to address remote resource."""
195
- # Defer import for circular dependencies
196
- return getS3Client ()
195
+ return getS3Client (self .profile )
196
+
197
+ @property
198
+ def profile (self ) -> str | None :
199
+ return self ._uri .username
200
+
201
+ @property
202
+ def bucket (self ) -> str :
203
+ bucket = self ._uri .hostname
204
+ if not bucket :
205
+ raise ValueError (f"S3 URI does not include bucket name: '{ str (self )} '" )
206
+
207
+ return bucket
197
208
198
209
@classmethod
199
210
def _mexists (cls , uris : Iterable [ResourcePath ]) -> dict [ResourcePath , bool ]:
200
211
# Force client to be created before creating threads.
201
- getS3Client ()
212
+ profiles = set [str | None ]()
213
+ for path in uris :
214
+ if path .scheme == "s3" :
215
+ path = cast (S3ResourcePath , path )
216
+ profiles .add (path .profile )
217
+ for profile in profiles :
218
+ getS3Client (profile )
202
219
203
220
return super ()._mexists (uris )
204
221
@@ -207,16 +224,16 @@ def exists(self) -> bool:
207
224
"""Check that the S3 resource exists."""
208
225
if self .is_root :
209
226
# Only check for the bucket since the path is irrelevant
210
- return bucketExists (self .netloc )
211
- exists , _ = s3CheckFileExists (self , client = self .client )
227
+ return bucketExists (self .bucket , self . client )
228
+ exists , _ = s3CheckFileExists (self , bucket = self . bucket , client = self .client )
212
229
return exists
213
230
214
231
@backoff .on_exception (backoff .expo , retryable_io_errors , max_time = max_retry_time )
215
232
def size (self ) -> int :
216
233
"""Return the size of the resource in bytes."""
217
234
if self .dirLike :
218
235
return 0
219
- exists , sz = s3CheckFileExists (self , client = self .client )
236
+ exists , sz = s3CheckFileExists (self , bucket = self . bucket , client = self .client )
220
237
if not exists :
221
238
raise FileNotFoundError (f"Resource { self } does not exist" )
222
239
return sz
@@ -229,7 +246,7 @@ def remove(self) -> None:
229
246
# for checking all the keys again, reponse is HTTP 204 OK
230
247
# response all the time
231
248
try :
232
- self .client .delete_object (Bucket = self .netloc , Key = self .relativeToPathRoot )
249
+ self .client .delete_object (Bucket = self .bucket , Key = self .relativeToPathRoot )
233
250
except (self .client .exceptions .NoSuchKey , self .client .exceptions .NoSuchBucket ) as err :
234
251
raise FileNotFoundError ("No such resource: {self}" ) from err
235
252
@@ -239,7 +256,7 @@ def read(self, size: int = -1) -> bytes:
239
256
if size > 0 :
240
257
args ["Range" ] = f"bytes=0-{ size - 1 } "
241
258
try :
242
- response = self .client .get_object (Bucket = self .netloc , Key = self .relativeToPathRoot , ** args )
259
+ response = self .client .get_object (Bucket = self .bucket , Key = self .relativeToPathRoot , ** args )
243
260
except (self .client .exceptions .NoSuchKey , self .client .exceptions .NoSuchBucket ) as err :
244
261
raise FileNotFoundError (f"No such resource: { self } " ) from err
245
262
except ClientError as err :
@@ -255,20 +272,20 @@ def write(self, data: bytes, overwrite: bool = True) -> None:
255
272
if not overwrite and self .exists ():
256
273
raise FileExistsError (f"Remote resource { self } exists and overwrite has been disabled" )
257
274
with time_this (log , msg = "Write to %s" , args = (self ,)):
258
- self .client .put_object (Bucket = self .netloc , Key = self .relativeToPathRoot , Body = data )
275
+ self .client .put_object (Bucket = self .bucket , Key = self .relativeToPathRoot , Body = data )
259
276
260
277
@backoff .on_exception (backoff .expo , all_retryable_errors , max_time = max_retry_time )
261
278
def mkdir (self ) -> None :
262
279
"""Write a directory key to S3."""
263
- if not bucketExists (self .netloc ):
264
- raise ValueError (f"Bucket { self .netloc } does not exist for { self } !" )
280
+ if not bucketExists (self .bucket , self . client ):
281
+ raise ValueError (f"Bucket { self .bucket } does not exist for { self } !" )
265
282
266
283
if not self .dirLike :
267
284
raise NotADirectoryError (f"Can not create a 'directory' for file-like URI { self } " )
268
285
269
286
# don't create S3 key when root is at the top-level of an Bucket
270
287
if self .path != "/" :
271
- self .client .put_object (Bucket = self .netloc , Key = self .relativeToPathRoot )
288
+ self .client .put_object (Bucket = self .bucket , Key = self .relativeToPathRoot )
272
289
273
290
@backoff .on_exception (backoff .expo , all_retryable_errors , max_time = max_retry_time )
274
291
def _download_file (self , local_file : IO , progress : ProgressPercentage | None ) -> None :
@@ -279,7 +296,7 @@ def _download_file(self, local_file: IO, progress: ProgressPercentage | None) ->
279
296
"""
280
297
try :
281
298
self .client .download_fileobj (
282
- self .netloc ,
299
+ self .bucket ,
283
300
self .relativeToPathRoot ,
284
301
local_file ,
285
302
Callback = progress ,
@@ -324,7 +341,7 @@ def _upload_file(self, local_file: ResourcePath, progress: ProgressPercentage |
324
341
"""
325
342
try :
326
343
self .client .upload_file (
327
- local_file .ospath , self .netloc , self .relativeToPathRoot , Callback = progress
344
+ local_file .ospath , self .bucket , self .relativeToPathRoot , Callback = progress
328
345
)
329
346
except self .client .exceptions .NoSuchBucket as err :
330
347
raise NotADirectoryError (f"Target does not exist: { err } " ) from err
@@ -333,13 +350,13 @@ def _upload_file(self, local_file: ResourcePath, progress: ProgressPercentage |
333
350
raise
334
351
335
352
@backoff .on_exception (backoff .expo , all_retryable_errors , max_time = max_retry_time )
336
- def _copy_from (self , src : ResourcePath ) -> None :
353
+ def _copy_from (self , src : S3ResourcePath ) -> None :
337
354
copy_source = {
338
- "Bucket" : src .netloc ,
355
+ "Bucket" : src .bucket ,
339
356
"Key" : src .relativeToPathRoot ,
340
357
}
341
358
try :
342
- self .client .copy_object (CopySource = copy_source , Bucket = self .netloc , Key = self .relativeToPathRoot )
359
+ self .client .copy_object (CopySource = copy_source , Bucket = self .bucket , Key = self .relativeToPathRoot )
343
360
except (self .client .exceptions .NoSuchKey , self .client .exceptions .NoSuchBucket ) as err :
344
361
raise FileNotFoundError ("No such resource to transfer: {self}" ) from err
345
362
except ClientError as err :
@@ -469,7 +486,7 @@ def walk(
469
486
filenames = []
470
487
files_there = False
471
488
472
- for page in s3_paginator .paginate (Bucket = self .netloc , Prefix = prefix , Delimiter = "/" ):
489
+ for page in s3_paginator .paginate (Bucket = self .bucket , Prefix = prefix , Delimiter = "/" ):
473
490
# All results are returned as full key names and we must
474
491
# convert them back to the root form. The prefix is fixed
475
492
# and delimited so that is a simple trim
@@ -507,7 +524,7 @@ def _openImpl(
507
524
* ,
508
525
encoding : str | None = None ,
509
526
) -> Iterator [ResourceHandleProtocol ]:
510
- with S3ResourceHandle (mode , log , self .client , self .netloc , self .relativeToPathRoot ) as handle :
527
+ with S3ResourceHandle (mode , log , self .client , self .bucket , self .relativeToPathRoot ) as handle :
511
528
if "b" in mode :
512
529
yield handle
513
530
else :
@@ -529,6 +546,6 @@ def generate_presigned_put_url(self, *, expiration_time_seconds: int) -> str:
529
546
def _generate_presigned_url (self , method : str , expiration_time_seconds : int ) -> str :
530
547
return self .client .generate_presigned_url (
531
548
method ,
532
- Params = {"Bucket" : self .netloc , "Key" : self .relativeToPathRoot },
549
+ Params = {"Bucket" : self .bucket , "Key" : self .relativeToPathRoot },
533
550
ExpiresIn = expiration_time_seconds ,
534
551
)
0 commit comments