@@ -19,15 +19,27 @@ class Bucket(object):
19
19
:type name: string
20
20
:param name: The name of the bucket.
21
21
"""
22
+ # ACL rules are lazily retrieved.
23
+ _acl = _default_object_acl = None
22
24
23
25
def __init__ (self , connection = None , name = None , metadata = None ):
24
26
self .connection = connection
25
27
self .name = name
26
28
self .metadata = metadata
27
29
28
- # ACL rules are lazily retrieved.
29
- self .acl = None
30
- self .default_object_acl = None
30
+ @property
31
+ def acl (self ):
32
+ """Create our ACL on demand."""
33
+ if self ._acl is None :
34
+ self ._acl = BucketACL (self )
35
+ return self ._acl
36
+
37
+ @property
38
+ def default_object_acl (self ):
39
+ """Create our defaultObjectACL on demand."""
40
+ if self ._default_object_acl is None :
41
+ self ._default_object_acl = DefaultObjectACL (self )
42
+ return self ._default_object_acl
31
43
32
44
@classmethod
33
45
def from_dict (cls , bucket_dict , connection = None ):
@@ -313,17 +325,15 @@ def has_metadata(self, field=None):
313
325
else :
314
326
return True
315
327
316
- def reload_metadata (self , full = False ):
328
+ def reload_metadata (self ):
317
329
"""Reload metadata from Cloud Storage.
318
330
319
- :type full: bool
320
- :param full: If True, loads all data (include ACL data).
321
-
322
331
:rtype: :class:`Bucket`
323
332
:returns: The bucket you just reloaded data for.
324
333
"""
325
- projection = 'full' if full else 'noAcl'
326
- query_params = {'projection' : projection }
334
+ # Pass only '?projection=noAcl' here because 'acl'/'defaultObjectAcl'
335
+ # are handled via 'get_acl()'/'get_default_object_acl()'
336
+ query_params = {'projection' : 'noAcl' }
327
337
self .metadata = self .connection .api_request (
328
338
method = 'GET' , path = self .path , query_params = query_params )
329
339
return self
@@ -344,9 +354,14 @@ def get_metadata(self, field=None, default=None):
344
354
:rtype: dict or anything
345
355
:returns: All metadata or the value of the specific field.
346
356
"""
357
+ if field == 'acl' :
358
+ raise KeyError ("Use 'get_acl()'" )
359
+
360
+ if field == 'defaultObjectAcl' :
361
+ raise KeyError ("Use 'get_default_object_acl()'" )
362
+
347
363
if not self .has_metadata (field = field ):
348
- full = (field and field in ('acl' , 'defaultObjectAcl' ))
349
- self .reload_metadata (full = full )
364
+ self .reload_metadata ()
350
365
351
366
if field :
352
367
return self .metadata .get (field , default )
@@ -431,11 +446,15 @@ def reload_acl(self):
431
446
:rtype: :class:`Bucket`
432
447
:returns: The current bucket.
433
448
"""
434
- self .acl = BucketACL (bucket = self )
449
+ self .acl .clear ()
450
+
451
+ url_path = '%s/acl' % self .path
452
+ found = self .connection .api_request (method = 'GET' , path = url_path )
453
+ for entry in found ['items' ]:
454
+ self .acl .add_entity (self .acl .entity_from_dict (entry ))
435
455
436
- for entry in self .get_metadata ('acl' , []):
437
- entity = self .acl .entity_from_dict (entry )
438
- self .acl .add_entity (entity )
456
+ # Even if we fetch no entries, the ACL is still loaded.
457
+ self .acl .loaded = True
439
458
440
459
return self
441
460
@@ -445,7 +464,7 @@ def get_acl(self):
445
464
:rtype: :class:`gcloud.storage.acl.BucketACL`
446
465
:returns: An ACL object for the current bucket.
447
466
"""
448
- if not self .acl :
467
+ if not self .acl . loaded :
449
468
self .reload_acl ()
450
469
return self .acl
451
470
@@ -487,12 +506,19 @@ def save_acl(self, acl=None):
487
506
# both evaluate to False, but mean very different things.
488
507
if acl is None :
489
508
acl = self .acl
509
+ dirty = acl .loaded
510
+ else :
511
+ dirty = True
490
512
491
- if acl is None :
492
- return self
513
+ if dirty :
514
+ result = self .connection .api_request (
515
+ method = 'PATCH' , path = self .path , data = {'acl' : list (acl )},
516
+ query_params = {'projection' : 'full' })
517
+ self .acl .clear ()
518
+ for entry in result ['acl' ]:
519
+ self .acl .entity (self .acl .entity_from_dict (entry ))
520
+ self .acl .loaded = True
493
521
494
- self .patch_metadata ({'acl' : list (acl )})
495
- self .reload_acl ()
496
522
return self
497
523
498
524
def clear_acl (self ):
@@ -522,19 +548,26 @@ def clear_acl(self):
522
548
523
549
At this point all the custom rules you created have been removed.
524
550
"""
525
- return self .save_acl (acl = [])
551
+ # NOTE: back-end makes some ACL entries sticky (they remain even
552
+ # after the PATCH succeeds.
553
+ return self .save_acl ([])
526
554
527
555
def reload_default_object_acl (self ):
528
556
"""Reload the Default Object ACL rules for this bucket.
529
557
530
558
:rtype: :class:`Bucket`
531
559
:returns: The current bucket.
532
560
"""
533
- self .default_object_acl = DefaultObjectACL (bucket = self )
561
+ doa = self .default_object_acl
562
+ doa .clear ()
534
563
535
- for entry in self .get_metadata ('defaultObjectAcl' , []):
536
- entity = self .default_object_acl .entity_from_dict (entry )
537
- self .default_object_acl .add_entity (entity )
564
+ url_path = '%s/defaultObjectAcl' % self .path
565
+ found = self .connection .api_request (method = 'GET' , path = url_path )
566
+ for entry in found ['items' ]:
567
+ doa .add_entity (doa .entity_from_dict (entry ))
568
+
569
+ # Even if we fetch no entries, the ACL is still loaded.
570
+ doa .loaded = True
538
571
539
572
return self
540
573
@@ -547,7 +580,7 @@ def get_default_object_acl(self):
547
580
:rtype: :class:`gcloud.storage.acl.DefaultObjectACL`
548
581
:returns: A DefaultObjectACL object for this bucket.
549
582
"""
550
- if not self .default_object_acl :
583
+ if not self .default_object_acl . loaded :
551
584
self .reload_default_object_acl ()
552
585
return self .default_object_acl
553
586
@@ -562,18 +595,26 @@ def save_default_object_acl(self, acl=None):
562
595
"""
563
596
if acl is None :
564
597
acl = self .default_object_acl
598
+ dirty = acl .loaded
599
+ else :
600
+ dirty = True
601
+
602
+ if dirty :
603
+ result = self .connection .api_request (
604
+ method = 'PATCH' , path = self .path ,
605
+ data = {'defaultObjectAcl' : list (acl )},
606
+ query_params = {'projection' : 'full' })
607
+ doa = self .default_object_acl
608
+ doa .clear ()
609
+ for entry in result ['defaultObjectAcl' ]:
610
+ doa .entity (doa .entity_from_dict (entry ))
611
+ doa .loaded = True
565
612
566
- if acl is None :
567
- return self
568
-
569
- self .patch_metadata ({'defaultObjectAcl' : list (acl )})
570
- self .reload_default_object_acl ()
571
613
return self
572
614
573
615
def clear_default_object_acl (self ):
574
616
"""Remove the Default Object ACL from this bucket."""
575
-
576
- return self .save_default_object_acl (acl = [])
617
+ return self .save_default_object_acl ([])
577
618
578
619
def make_public (self , recursive = False , future = False ):
579
620
"""Make a bucket public.
0 commit comments