@@ -1188,6 +1188,7 @@ def get_blob(
11881188 if_metageneration_not_match = None ,
11891189 timeout = _DEFAULT_TIMEOUT ,
11901190 retry = DEFAULT_RETRY ,
1191+ soft_deleted = None ,
11911192 ** kwargs ,
11921193 ):
11931194 """Get a blob object by name.
@@ -1248,6 +1249,13 @@ def get_blob(
12481249 :param retry:
12491250 (Optional) How to retry the RPC. See: :ref:`configuring_retries`
12501251
1252+ :type soft_deleted: bool
1253+ :param soft_deleted:
1254+ (Optional) If True, looks for a soft-deleted object. Will only return
1255+ the object metadata if the object exists and is in a soft-deleted state.
1256+ Object ``generation`` is required if ``soft_deleted`` is set to True.
1257+ See: https://cloud.google.com/storage/docs/soft-delete
1258+
12511259 :param kwargs: Keyword arguments to pass to the
12521260 :class:`~google.cloud.storage.blob.Blob` constructor.
12531261
@@ -1275,6 +1283,7 @@ def get_blob(
12751283 if_metageneration_match = if_metageneration_match ,
12761284 if_metageneration_not_match = if_metageneration_not_match ,
12771285 retry = retry ,
1286+ soft_deleted = soft_deleted ,
12781287 )
12791288 except NotFound :
12801289 return None
@@ -1297,6 +1306,7 @@ def list_blobs(
12971306 timeout = _DEFAULT_TIMEOUT ,
12981307 retry = DEFAULT_RETRY ,
12991308 match_glob = None ,
1309+ soft_deleted = None ,
13001310 ):
13011311 """Return an iterator used to find blobs in the bucket.
13021312
@@ -1378,6 +1388,13 @@ def list_blobs(
13781388 The string value must be UTF-8 encoded. See:
13791389 https://cloud.google.com/storage/docs/json_api/v1/objects/list#list-object-glob
13801390
1391+ :type soft_deleted: bool
1392+ :param soft_deleted:
1393+ (Optional) If true, only soft-deleted objects will be listed as distinct results in order of increasing
1394+ generation number. This parameter can only be used successfully if the bucket has a soft delete policy.
1395+ Note ``soft_deleted`` and ``versions`` cannot be set to True simultaneously. See:
1396+ https://cloud.google.com/storage/docs/soft-delete
1397+
13811398 :rtype: :class:`~google.api_core.page_iterator.Iterator`
13821399 :returns: Iterator of all :class:`~google.cloud.storage.blob.Blob`
13831400 in this bucket matching the arguments.
@@ -1398,6 +1415,7 @@ def list_blobs(
13981415 timeout = timeout ,
13991416 retry = retry ,
14001417 match_glob = match_glob ,
1418+ soft_deleted = soft_deleted ,
14011419 )
14021420
14031421 def list_notifications (
@@ -2060,6 +2078,110 @@ def rename_blob(
20602078 )
20612079 return new_blob
20622080
2081+ def restore_blob (
2082+ self ,
2083+ blob_name ,
2084+ client = None ,
2085+ generation = None ,
2086+ copy_source_acl = None ,
2087+ projection = None ,
2088+ if_generation_match = None ,
2089+ if_generation_not_match = None ,
2090+ if_metageneration_match = None ,
2091+ if_metageneration_not_match = None ,
2092+ timeout = _DEFAULT_TIMEOUT ,
2093+ retry = DEFAULT_RETRY_IF_GENERATION_SPECIFIED ,
2094+ ):
2095+ """Restores a soft-deleted object.
2096+
2097+ If :attr:`user_project` is set on the bucket, bills the API request to that project.
2098+
2099+ See [API reference docs](https://cloud.google.com/storage/docs/json_api/v1/objects/restore)
2100+
2101+ :type blob_name: str
2102+ :param blob_name: The name of the blob to be restored.
2103+
2104+ :type client: :class:`~google.cloud.storage.client.Client`
2105+ :param client: (Optional) The client to use. If not passed, falls back
2106+ to the ``client`` stored on the current bucket.
2107+
2108+ :type generation: long
2109+ :param generation: (Optional) If present, selects a specific revision of this object.
2110+
2111+ :type copy_source_acl: bool
2112+ :param copy_source_acl: (Optional) If true, copy the soft-deleted object's access controls.
2113+
2114+ :type projection: str
2115+ :param projection: (Optional) Specifies the set of properties to return.
2116+ If used, must be 'full' or 'noAcl'.
2117+
2118+ :type if_generation_match: long
2119+ :param if_generation_match:
2120+ (Optional) See :ref:`using-if-generation-match`
2121+
2122+ :type if_generation_not_match: long
2123+ :param if_generation_not_match:
2124+ (Optional) See :ref:`using-if-generation-not-match`
2125+
2126+ :type if_metageneration_match: long
2127+ :param if_metageneration_match:
2128+ (Optional) See :ref:`using-if-metageneration-match`
2129+
2130+ :type if_metageneration_not_match: long
2131+ :param if_metageneration_not_match:
2132+ (Optional) See :ref:`using-if-metageneration-not-match`
2133+
2134+ :type timeout: float or tuple
2135+ :param timeout:
2136+ (Optional) The amount of time, in seconds, to wait
2137+ for the server response. See: :ref:`configuring_timeouts`
2138+
2139+ :type retry: google.api_core.retry.Retry or google.cloud.storage.retry.ConditionalRetryPolicy
2140+ :param retry:
2141+ (Optional) How to retry the RPC.
2142+ The default value is ``DEFAULT_RETRY_IF_GENERATION_SPECIFIED``, which
2143+ only restore operations with ``if_generation_match`` or ``generation`` set
2144+ will be retried.
2145+
2146+ Users can configure non-default retry behavior. A ``None`` value will
2147+ disable retries. A ``DEFAULT_RETRY`` value will enable retries
2148+ even if restore operations are not guaranteed to be idempotent.
2149+ See [Configuring Retries](https://cloud.google.com/python/docs/reference/storage/latest/retry_timeout).
2150+
2151+ :rtype: :class:`google.cloud.storage.blob.Blob`
2152+ :returns: The restored Blob.
2153+ """
2154+ client = self ._require_client (client )
2155+ query_params = {}
2156+
2157+ if self .user_project is not None :
2158+ query_params ["userProject" ] = self .user_project
2159+ if generation is not None :
2160+ query_params ["generation" ] = generation
2161+ if copy_source_acl is not None :
2162+ query_params ["copySourceAcl" ] = copy_source_acl
2163+ if projection is not None :
2164+ query_params ["projection" ] = projection
2165+
2166+ _add_generation_match_parameters (
2167+ query_params ,
2168+ if_generation_match = if_generation_match ,
2169+ if_generation_not_match = if_generation_not_match ,
2170+ if_metageneration_match = if_metageneration_match ,
2171+ if_metageneration_not_match = if_metageneration_not_match ,
2172+ )
2173+
2174+ blob = Blob (bucket = self , name = blob_name )
2175+ api_response = client ._post_resource (
2176+ f"{ blob .path } /restore" ,
2177+ None ,
2178+ query_params = query_params ,
2179+ timeout = timeout ,
2180+ retry = retry ,
2181+ )
2182+ blob ._set_properties (api_response )
2183+ return blob
2184+
20632185 @property
20642186 def cors (self ):
20652187 """Retrieve or set CORS policies configured for this bucket.
@@ -2227,6 +2349,18 @@ def iam_configuration(self):
22272349 info = self ._properties .get ("iamConfiguration" , {})
22282350 return IAMConfiguration .from_api_repr (info , self )
22292351
2352+ @property
2353+ def soft_delete_policy (self ):
2354+ """Retrieve the soft delete policy for this bucket.
2355+
2356+ See https://cloud.google.com/storage/docs/soft-delete
2357+
2358+ :rtype: :class:`SoftDeletePolicy`
2359+ :returns: an instance for managing the bucket's soft delete policy.
2360+ """
2361+ policy = self ._properties .get ("softDeletePolicy" , {})
2362+ return SoftDeletePolicy .from_api_repr (policy , self )
2363+
22302364 @property
22312365 def lifecycle_rules (self ):
22322366 """Retrieve or set lifecycle rules configured for this bucket.
@@ -3432,6 +3566,102 @@ def generate_signed_url(
34323566 )
34333567
34343568
3569+ class SoftDeletePolicy (dict ):
3570+ """Map a bucket's soft delete policy.
3571+
3572+ See https://cloud.google.com/storage/docs/soft-delete
3573+
3574+ :type bucket: :class:`Bucket`
3575+ :param bucket: Bucket for which this instance is the policy.
3576+
3577+ :type retention_duration_seconds: int
3578+ :param retention_duration_seconds:
3579+ (Optional) The period of time in seconds that soft-deleted objects in the bucket
3580+ will be retained and cannot be permanently deleted.
3581+
3582+ :type effective_time: :class:`datetime.datetime`
3583+ :param effective_time:
3584+ (Optional) When the bucket's soft delete policy is effective.
3585+ This value should normally only be set by the back-end API.
3586+ """
3587+
3588+ def __init__ (self , bucket , ** kw ):
3589+ data = {}
3590+ retention_duration_seconds = kw .get ("retention_duration_seconds" )
3591+ data ["retentionDurationSeconds" ] = retention_duration_seconds
3592+
3593+ effective_time = kw .get ("effective_time" )
3594+ if effective_time is not None :
3595+ effective_time = _datetime_to_rfc3339 (effective_time )
3596+ data ["effectiveTime" ] = effective_time
3597+
3598+ super ().__init__ (data )
3599+ self ._bucket = bucket
3600+
3601+ @classmethod
3602+ def from_api_repr (cls , resource , bucket ):
3603+ """Factory: construct instance from resource.
3604+
3605+ :type resource: dict
3606+ :param resource: mapping as returned from API call.
3607+
3608+ :type bucket: :class:`Bucket`
3609+ :params bucket: Bucket for which this instance is the policy.
3610+
3611+ :rtype: :class:`SoftDeletePolicy`
3612+ :returns: Instance created from resource.
3613+ """
3614+ instance = cls (bucket )
3615+ instance .update (resource )
3616+ return instance
3617+
3618+ @property
3619+ def bucket (self ):
3620+ """Bucket for which this instance is the policy.
3621+
3622+ :rtype: :class:`Bucket`
3623+ :returns: the instance's bucket.
3624+ """
3625+ return self ._bucket
3626+
3627+ @property
3628+ def retention_duration_seconds (self ):
3629+ """Get the retention duration of the bucket's soft delete policy.
3630+
3631+ :rtype: int or ``NoneType``
3632+ :returns: The period of time in seconds that soft-deleted objects in the bucket
3633+ will be retained and cannot be permanently deleted; Or ``None`` if the
3634+ property is not set.
3635+ """
3636+ duration = self .get ("retentionDurationSeconds" )
3637+ if duration is not None :
3638+ return int (duration )
3639+
3640+ @retention_duration_seconds .setter
3641+ def retention_duration_seconds (self , value ):
3642+ """Set the retention duration of the bucket's soft delete policy.
3643+
3644+ :type value: int
3645+ :param value:
3646+ The period of time in seconds that soft-deleted objects in the bucket
3647+ will be retained and cannot be permanently deleted.
3648+ """
3649+ self ["retentionDurationSeconds" ] = value
3650+ self .bucket ._patch_property ("softDeletePolicy" , self )
3651+
3652+ @property
3653+ def effective_time (self ):
3654+ """Get the effective time of the bucket's soft delete policy.
3655+
3656+ :rtype: datetime.datetime or ``NoneType``
3657+ :returns: point-in time at which the bucket's soft delte policy is
3658+ effective, or ``None`` if the property is not set.
3659+ """
3660+ timestamp = self .get ("effectiveTime" )
3661+ if timestamp is not None :
3662+ return _rfc3339_nanos_to_datetime (timestamp )
3663+
3664+
34353665def _raise_if_len_differs (expected_len , ** generation_match_args ):
34363666 """
34373667 Raise an error if any generation match argument
0 commit comments