@@ -317,19 +317,148 @@ def list_all_buckets(self):
317
317
response ["list" ] = getListFromXml (response ["data" ], "Bucket" )
318
318
return response
319
319
320
- def bucket_list (self , bucket , prefix = None , recursive = None , uri_params = None , limit = - 1 ):
320
+ def bucket_list (
321
+ self ,
322
+ bucket ,
323
+ prefix = None ,
324
+ recursive = None ,
325
+ uri_params = None ,
326
+ limit = - 1 ,
327
+ list_objects_v2 = False
328
+ ):
329
+ if uri_params is None :
330
+ uri_params = {}
331
+ if uri_params .get ("list_type" ) == "v2" :
332
+ list_objects_v2 = True
333
+
321
334
item_list = []
322
335
prefixes = []
323
- for truncated , dirs , objects in self .bucket_list_streaming (bucket , prefix , recursive , uri_params , limit ):
324
- item_list .extend (objects )
325
- prefixes .extend (dirs )
336
+ if list_objects_v2 :
337
+ uri_params .update ({"list_type" : "v2" })
338
+ for truncated , dirs , objects in self .bucket_list_v2_streaming (
339
+ bucket ,
340
+ prefix ,
341
+ recursive ,
342
+ uri_params ,
343
+ limit
344
+ ):
345
+ item_list .extend (objects )
346
+ prefixes .extend (dirs )
347
+ else :
348
+ for truncated , dirs , objects in self .bucket_list_streaming (
349
+ bucket ,
350
+ prefix ,
351
+ recursive ,
352
+ uri_params ,
353
+ limit
354
+ ):
355
+ item_list .extend (objects )
356
+ prefixes .extend (dirs )
326
357
327
358
response = {}
328
359
response ['list' ] = item_list
329
360
response ['common_prefixes' ] = prefixes
330
361
response ['truncated' ] = truncated
331
362
return response
332
363
364
+ def bucket_list_v2_streaming (
365
+ self ,
366
+ bucket ,
367
+ prefix = None ,
368
+ recursive = None ,
369
+ uri_params = {},
370
+ limit = - 1 ,
371
+ ):
372
+ def _list_truncated (data ):
373
+ # <IsTruncated> can either be "true" or "false" or be missing completely
374
+ is_truncated = getTextFromXml (data , ".//IsTruncated" ) or "false"
375
+ return is_truncated .lower () != "false"
376
+
377
+ def _get_contents (data ):
378
+ return getListFromXml (data , "Contents" )
379
+
380
+ def _get_common_prefixes (data ):
381
+ return getListFromXml (data , "CommonPrefixes" )
382
+
383
+ def _get_next_continuation_token (data ):
384
+ return getTextFromXml (data , "NextContinuationToken" )
385
+
386
+ uri_params = uri_params and uri_params .copy () or {}
387
+ truncated = True
388
+
389
+ num_objects = 0
390
+ num_prefixes = 0
391
+ max_keys = limit
392
+ next_continuation_token = ""
393
+ while truncated :
394
+ if next_continuation_token :
395
+ response = self .bucket_list_v2_noparse (
396
+ bucket ,
397
+ prefix ,
398
+ recursive ,
399
+ uri_params ,
400
+ max_keys ,
401
+ next_continuation_token
402
+ )
403
+ else :
404
+ response = self .bucket_list_v2_noparse (
405
+ bucket ,
406
+ prefix ,
407
+ recursive ,
408
+ uri_params ,
409
+ max_keys
410
+ )
411
+ current_list = _get_contents (response ["data" ])
412
+ current_prefixes = _get_common_prefixes (response ["data" ])
413
+ num_objects += len (current_list )
414
+ num_prefixes += len (current_prefixes )
415
+ if limit > num_objects + num_prefixes :
416
+ max_keys = limit - (num_objects + num_prefixes )
417
+ truncated = _list_truncated (response ["data" ])
418
+ if truncated :
419
+ if limit == - 1 or num_objects + num_prefixes < limit :
420
+ if current_list or current_prefixes :
421
+ next_continuation_token = _get_next_continuation_token (
422
+ response ["data" ]
423
+ )
424
+ else :
425
+ # Unexpectedly, the server lied, and so the previous
426
+ # response was not truncated. So, no new key to get.
427
+ yield False , current_prefixes , current_list
428
+ break
429
+ else :
430
+ yield truncated , current_prefixes , current_list
431
+ break
432
+
433
+ yield truncated , current_prefixes , current_list
434
+
435
+ def bucket_list_v2_noparse (
436
+ self ,
437
+ bucket ,
438
+ prefix = None ,
439
+ recursive = None ,
440
+ uri_params = {},
441
+ max_keys = - 1 ,
442
+ continuation_token = None
443
+ ):
444
+ if prefix :
445
+ uri_params ['prefix' ] = prefix
446
+ if not self .config .recursive and not recursive :
447
+ uri_params ['delimiter' ] = "/"
448
+ if max_keys != - 1 :
449
+ uri_params ['max-keys' ] = str (max_keys )
450
+ if self .config .list_allow_unordered :
451
+ uri_params ['allow-unordered' ] = "true"
452
+ if continuation_token :
453
+ uri_params ["continuation-token" ] = continuation_token
454
+ request = self .create_request (
455
+ "BUCKET_LIST" ,
456
+ bucket = bucket ,
457
+ uri_params = uri_params
458
+ )
459
+ response = self .send_request (request )
460
+ return response
461
+
333
462
def bucket_list_streaming (self , bucket , prefix = None , recursive = None , uri_params = None , limit = - 1 ):
334
463
""" Generator that produces <dir_list>, <object_list> pairs of groups of content of a specified bucket. """
335
464
def _list_truncated (data ):
@@ -383,9 +512,7 @@ def _get_next_marker(data, current_elts, key):
383
512
384
513
yield truncated , current_prefixes , current_list
385
514
386
- def bucket_list_noparse (self , bucket , prefix = None , recursive = None , uri_params = None , max_keys = - 1 ):
387
- if uri_params is None :
388
- uri_params = {}
515
+ def bucket_list_noparse (self , bucket , prefix = None , recursive = None , uri_params = {}, max_keys = - 1 ):
389
516
if prefix :
390
517
uri_params ['prefix' ] = prefix
391
518
if not self .config .recursive and not recursive :
0 commit comments