3030
3131_DEFAULT_STORAGE_HOST = u"https://storage.googleapis.com"
3232
33+ # generation match parameters in camel and snake cases
34+ _GENERATION_MATCH_PARAMETERS = (
35+ ("if_generation_match" , "ifGenerationMatch" ),
36+ ("if_generation_not_match" , "ifGenerationNotMatch" ),
37+ ("if_metageneration_match" , "ifMetagenerationMatch" ),
38+ ("if_metageneration_not_match" , "ifMetagenerationNotMatch" ),
39+ ("if_source_generation_match" , "ifSourceGenerationMatch" ),
40+ ("if_source_generation_not_match" , "ifSourceGenerationNotMatch" ),
41+ ("if_source_metageneration_match" , "ifSourceMetagenerationMatch" ),
42+ ("if_source_metageneration_not_match" , "ifSourceMetagenerationNotMatch" ),
43+ )
44+
3345
3446def _get_storage_host ():
3547 return os .environ .get (STORAGE_EMULATOR_ENV_VAR , _DEFAULT_STORAGE_HOST )
@@ -121,27 +133,64 @@ def _query_params(self):
121133 params ["userProject" ] = self .user_project
122134 return params
123135
124- def reload (self , client = None , timeout = _DEFAULT_TIMEOUT ):
136+ def reload (
137+ self ,
138+ client = None ,
139+ timeout = _DEFAULT_TIMEOUT ,
140+ if_generation_match = None ,
141+ if_generation_not_match = None ,
142+ if_metageneration_match = None ,
143+ if_metageneration_not_match = None ,
144+ ):
125145 """Reload properties from Cloud Storage.
126146
127147 If :attr:`user_project` is set, bills the API request to that project.
128148
129149 :type client: :class:`~google.cloud.storage.client.Client` or
130150 ``NoneType``
131- :param client: the client to use. If not passed, falls back to the
151+ :param client: the client to use. If not passed, falls back to the
132152 ``client`` stored on the current object.
153+
133154 :type timeout: float or tuple
134155 :param timeout: (Optional) The amount of time, in seconds, to wait
135156 for the server response.
136157
137158 Can also be passed as a tuple (connect_timeout, read_timeout).
138159 See :meth:`requests.Session.request` documentation for details.
160+
161+ :type if_generation_match: long
162+ :param if_generation_match: (Optional) Make the operation conditional on whether
163+ the blob's current generation matches the given value.
164+ Setting to 0 makes the operation succeed only if there
165+ are no live versions of the blob.
166+
167+ :type if_generation_not_match: long
168+ :param if_generation_not_match: (Optional) Make the operation conditional on whether
169+ the blob's current generation does not match the given
170+ value. If no live blob exists, the precondition fails.
171+ Setting to 0 makes the operation succeed only if there
172+ is a live version of the blob.
173+
174+ :type if_metageneration_match: long
175+ :param if_metageneration_match: (Optional) Make the operation conditional on whether the
176+ blob's current metageneration matches the given value.
177+
178+ :type if_metageneration_not_match: long
179+ :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
180+ blob's current metageneration does not match the given value.
139181 """
140182 client = self ._require_client (client )
141183 query_params = self ._query_params
142184 # Pass only '?projection=noAcl' here because 'acl' and related
143185 # are handled via custom endpoints.
144186 query_params ["projection" ] = "noAcl"
187+ _add_generation_match_parameters (
188+ query_params ,
189+ if_generation_match = if_generation_match ,
190+ if_generation_not_match = if_generation_not_match ,
191+ if_metageneration_match = if_metageneration_match ,
192+ if_metageneration_not_match = if_metageneration_not_match ,
193+ )
145194 api_response = client ._connection .api_request (
146195 method = "GET" ,
147196 path = self .path ,
@@ -180,7 +229,15 @@ def _set_properties(self, value):
180229 # If the values are reset, the changes must as well.
181230 self ._changes = set ()
182231
183- def patch (self , client = None , timeout = _DEFAULT_TIMEOUT ):
232+ def patch (
233+ self ,
234+ client = None ,
235+ timeout = _DEFAULT_TIMEOUT ,
236+ if_generation_match = None ,
237+ if_generation_not_match = None ,
238+ if_metageneration_match = None ,
239+ if_metageneration_not_match = None ,
240+ ):
184241 """Sends all changed properties in a PATCH request.
185242
186243 Updates the ``_properties`` with the response from the backend.
@@ -189,20 +246,49 @@ def patch(self, client=None, timeout=_DEFAULT_TIMEOUT):
189246
190247 :type client: :class:`~google.cloud.storage.client.Client` or
191248 ``NoneType``
192- :param client: the client to use. If not passed, falls back to the
249+ :param client: the client to use. If not passed, falls back to the
193250 ``client`` stored on the current object.
251+
194252 :type timeout: float or tuple
195253 :param timeout: (Optional) The amount of time, in seconds, to wait
196254 for the server response.
197255
198256 Can also be passed as a tuple (connect_timeout, read_timeout).
199257 See :meth:`requests.Session.request` documentation for details.
258+
259+ :type if_generation_match: long
260+ :param if_generation_match: (Optional) Make the operation conditional on whether
261+ the blob's current generation matches the given value.
262+ Setting to 0 makes the operation succeed only if there
263+ are no live versions of the blob.
264+
265+ :type if_generation_not_match: long
266+ :param if_generation_not_match: (Optional) Make the operation conditional on whether
267+ the blob's current generation does not match the given
268+ value. If no live blob exists, the precondition fails.
269+ Setting to 0 makes the operation succeed only if there
270+ is a live version of the blob.
271+
272+ :type if_metageneration_match: long
273+ :param if_metageneration_match: (Optional) Make the operation conditional on whether the
274+ blob's current metageneration matches the given value.
275+
276+ :type if_metageneration_not_match: long
277+ :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
278+ blob's current metageneration does not match the given value.
200279 """
201280 client = self ._require_client (client )
202281 query_params = self ._query_params
203282 # Pass '?projection=full' here because 'PATCH' documented not
204283 # to work properly w/ 'noAcl'.
205284 query_params ["projection" ] = "full"
285+ _add_generation_match_parameters (
286+ query_params ,
287+ if_generation_match = if_generation_match ,
288+ if_generation_not_match = if_generation_not_match ,
289+ if_metageneration_match = if_metageneration_match ,
290+ if_metageneration_not_match = if_metageneration_not_match ,
291+ )
206292 update_properties = {key : self ._properties [key ] for key in self ._changes }
207293
208294 # Make the API call.
@@ -216,7 +302,15 @@ def patch(self, client=None, timeout=_DEFAULT_TIMEOUT):
216302 )
217303 self ._set_properties (api_response )
218304
219- def update (self , client = None , timeout = _DEFAULT_TIMEOUT ):
305+ def update (
306+ self ,
307+ client = None ,
308+ timeout = _DEFAULT_TIMEOUT ,
309+ if_generation_match = None ,
310+ if_generation_not_match = None ,
311+ if_metageneration_match = None ,
312+ if_metageneration_not_match = None ,
313+ ):
220314 """Sends all properties in a PUT request.
221315
222316 Updates the ``_properties`` with the response from the backend.
@@ -225,18 +319,46 @@ def update(self, client=None, timeout=_DEFAULT_TIMEOUT):
225319
226320 :type client: :class:`~google.cloud.storage.client.Client` or
227321 ``NoneType``
228- :param client: the client to use. If not passed, falls back to the
322+ :param client: the client to use. If not passed, falls back to the
229323 ``client`` stored on the current object.
324+
230325 :type timeout: float or tuple
231326 :param timeout: (Optional) The amount of time, in seconds, to wait
232327 for the server response.
233328
234329 Can also be passed as a tuple (connect_timeout, read_timeout).
235330 See :meth:`requests.Session.request` documentation for details.
331+
332+ :type if_generation_match: long
333+ :param if_generation_match: (Optional) Make the operation conditional on whether
334+ the blob's current generation matches the given value.
335+ Setting to 0 makes the operation succeed only if there
336+ are no live versions of the blob.
337+
338+ :type if_generation_not_match: long
339+ :param if_generation_not_match: (Optional) Make the operation conditional on whether
340+ the blob's current generation does not match the given
341+ value. If no live blob exists, the precondition fails.
342+ Setting to 0 makes the operation succeed only if there
343+ is a live version of the blob.
344+
345+ :type if_metageneration_match: long
346+ :param if_metageneration_match: (Optional) Make the operation conditional on whether the
347+ blob's current metageneration matches the given value.
348+
349+ :type if_metageneration_not_match: long
350+ :param if_metageneration_not_match: (Optional) Make the operation conditional on whether the
351+ blob's current metageneration does not match the given value.
236352 """
237353 client = self ._require_client (client )
354+
238355 query_params = self ._query_params
239356 query_params ["projection" ] = "full"
357+ _add_generation_match_parameters (
358+ query_params ,
359+ if_metageneration_match = if_metageneration_match ,
360+ if_metageneration_not_match = if_metageneration_not_match ,
361+ )
240362 api_response = client ._connection .api_request (
241363 method = "PUT" ,
242364 path = self .path ,
@@ -312,3 +434,52 @@ def _convert_to_timestamp(value):
312434 utc_naive = value .replace (tzinfo = None ) - value .utcoffset ()
313435 mtime = (utc_naive - datetime (1970 , 1 , 1 )).total_seconds ()
314436 return mtime
437+
438+
439+ def _add_generation_match_parameters (parameters , ** match_parameters ):
440+ """Add generation match parameters into the given parameters list.
441+
442+ :type parameters: list or dict
443+ :param parameters: Parameters list or dict.
444+
445+ :type match_parameters: dict
446+ :param match_parameters: if*generation*match parameters to add.
447+
448+ :raises: :exc:`ValueError` if ``parameters`` is not a ``list()``
449+ or a ``dict()``.
450+ """
451+ for snakecase_name , camelcase_name in _GENERATION_MATCH_PARAMETERS :
452+ value = match_parameters .get (snakecase_name )
453+
454+ if value is not None :
455+ if isinstance (parameters , list ):
456+ parameters .append ((camelcase_name , value ))
457+
458+ elif isinstance (parameters , dict ):
459+ parameters [camelcase_name ] = value
460+
461+ else :
462+ raise ValueError (
463+ "`parameters` argument should be a dict() or a list()."
464+ )
465+
466+
467+ def _raise_if_more_than_one_set (** kwargs ):
468+ """Raise ``ValueError`` exception if more than one parameter was set.
469+
470+ :type error: :exc:`ValueError`
471+ :param error: Description of which fields were set
472+
473+ :raises: :class:`~ValueError` containing the fields that were set
474+ """
475+ if sum (arg is not None for arg in kwargs .values ()) > 1 :
476+ escaped_keys = ["'%s'" % name for name in kwargs .keys ()]
477+
478+ keys_but_last = ", " .join (escaped_keys [:- 1 ])
479+ last_key = escaped_keys [- 1 ]
480+
481+ msg = "Pass at most one of {keys_but_last} and {last_key}" .format (
482+ keys_but_last = keys_but_last , last_key = last_key
483+ )
484+
485+ raise ValueError (msg )
0 commit comments