Add missing generation arguments for bucket and blob methods.#7023
Add missing generation arguments for bucket and blob methods.#7023jmcarp wants to merge 4 commits intogoogleapis:masterfrom
Conversation
|
What is implemented here looks OK, but seems like it might not be comprehensive enough (e.g., should @frankyn PTAL. |
|
Thanks @tseaver. I took a stab at adding generations to mixed-in methods like |
|
Thanks for the ping. Looking over it now. |
frankyn
left a comment
There was a problem hiding this comment.
I did a first pass. Let's follow-up next week. Thank you for your patience.
| return self.bucket.user_project | ||
|
|
||
| @property | ||
| def query_params(self): |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
| return params | ||
|
|
||
| def blob(self, blob_name, chunk_size=None, encryption_key=None, kms_key_name=None): | ||
| """Factory constructor for blob object. |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
|
Thanks @frankyn. Updated the PR. |
frankyn
left a comment
There was a problem hiding this comment.
Please add system tests for this feature as well in (https://github.com/googleapis/google-cloud-python/blob/master/storage/tests/system.py) with respect to a generation:
- blob.update()
- blob.patch()
- blob.delete()
- bucket.get_object()
| connection = _Connection({"name": BLOB_NAME}) | ||
| client = _Client(connection) | ||
| bucket = self._make_one(name=NAME) | ||
| blob = bucket.get_blob(BLOB_NAME, client=client, generation=GENERATION) |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
2aa77a3 to
31174f6
Compare
|
Updated. Looks like we already exercise the |
|
Ping @frankyn when you have time; ready for another look. |
| blob0.content_language = "en" | ||
| blob0.patch() | ||
| self.assertEqual(blob0.content_language, "en") | ||
| self.assertIsNone(blob1.content_language) |
There was a problem hiding this comment.
Is this required? Seems unnecessary based on the previous line.
There was a problem hiding this comment.
We're patching the language of the 0th generation of the blob, so we expect the metadata of the 1st generation to still be None.
There was a problem hiding this comment.
ahhh I missed that it's blob1 and not blob0 thank you.
| blob0.metadata = metadata | ||
| blob0.update() | ||
| self.assertEqual(blob0.metadata, metadata) | ||
| self.assertIsNone(blob1.metadata) |
There was a problem hiding this comment.
Same here, doesn't look necessary if metadata for sure is not None.
|
Anything else I should do on this @frankyn @JustinBeckwith? |
|
@crwilcox I'm good with the merge wdyt? |
| query_params["generation"] = self.generation | ||
|
|
||
| if self.user_project is not None: | ||
| query_params["userProject"] = self.user_project |
There was a problem hiding this comment.
Seems like this block should use the _query_params property, e.g.:
# minimize the returned payload.
query_params = self._query_params
query_params["fields"] = "name"|
|
||
| if self.user_project is not None: | ||
| query_params["userProject"] = self.user_project | ||
|
|
There was a problem hiding this comment.
Likewise here:
query_params = self._query_params
if token:
query_params["rewriteToken"] = token
if source.generation:
query_params["sourceGeneration"] = source.generation
if self.kms_key_name is not None:
query_params["destinationKmsKeyName"] = self.kms_key_name| params = {} | ||
| if self.user_project is not None: | ||
| params["userProject"] = self.user_project | ||
| return params |
There was a problem hiding this comment.
This method is shadowed by both Blob and Bucket: let's make it virtual, e.g.:
@property
def _query_params(self):
"""Default query parameters."""
raise NotImplementedErrorThat way we don't have to add test coverage for it, either.
There was a problem hiding this comment.
Looking at how it is used in reload, I've changed my mind: lets just get rid of the duplicate version in Bucket.
|
|
||
| blob = Blob( | ||
| bucket=self, name=blob_name, encryption_key=encryption_key, **kwargs | ||
| ) |
There was a problem hiding this comment.
I'd rather create the blob with the expected generation here, and then use its _query_params property, e.g.:
client = self._require_client(client)
blob = Blob(
bucket=self, name=blob_name, encryption_key=encryption_key, generation=generation, **kwargs
)
query_params = blob._query_params
try:
headers = _get_encryption_headers(encryption_key)
response = client._connection.api_request(
method="GET",
path=blob.path,
query_params=query_params,
headers=headers,
_target_object=blob,
)Now that I look at it more closely, I'm not sure why this method doesn't just call blob.reload().
| @@ -825,6 +859,9 @@ def delete_blob(self, blob_name, client=None): | |||
| client = self._require_client(client) | |||
| query_params = {} | |||
There was a problem hiding this comment.
Use self._query_params here as well, e.g.:
query_params = self._query_params
if generation is not None:
query_params["generation"] = generation
blob_path = Blob.path_helper(self.path, blob_name)| params = {} | ||
| if self.user_project is not None: | ||
| params["userProject"] = self.user_project | ||
| return params |
There was a problem hiding this comment.
Please add explicit test coverage for the branches in this property.
There was a problem hiding this comment.
Per above, let's just delete this version of the property, which duplicates the one in the base class.
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import io |
There was a problem hiding this comment.
Import will be unused if you switch to blob.upload_from_string below.
| blob.delete() | ||
| self.assertFalse(blob.exists()) | ||
| self.assertEqual(bucket._deleted, [(BLOB_NAME, None)]) | ||
| self.assertEqual(bucket._deleted, [(BLOB_NAME, None, blob.generation)]) |
There was a problem hiding this comment.
Please duplicate this test, once with an explicit generation, and make this assertion be clear for both cases (None for the case where the blob is created without a generation).
| client = _Client(connection) | ||
| source_bucket = _Bucket(client=client) | ||
| source_blob = self._make_one(SOURCE_BLOB, bucket=source_bucket) | ||
| source_blob._set_properties({"generation": SOURCE_GENERATION}) |
There was a problem hiding this comment.
Please pass the generation to _make_one, e.g.:
source_blob = self._make_one(SOURCE_BLOB, bucket=source_bucket, generation=GENERATION)| NAME = "name" | ||
| BLOB_NAME = "blob-name" | ||
| GENERATION = 1512565576797178 | ||
| connection = _Connection({"name": BLOB_NAME}) |
There was a problem hiding this comment.
For clarity, add the generation to the properties here, and then assert that it is read in correctly below:
connection = _Connection({"name": BLOB_NAME, generation=GENERATION})
Allow setting generation for getting and deleting blobs, and respect generation for rewriting.