Skip to content

Commit 49ba14c

Browse files
fix: populate etag / generation / metageneration properties during download (#488)
Populate properties during download functions via Blob._extract_headers_from_download. Additionally, update documentation for running system tests to use Python 3.8 instead of Python 3.7 to align with nox. Closes #490.
1 parent 1ae766e commit 49ba14c

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

CONTRIBUTING.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,13 @@ Running System Tests
133133

134134
- To run system tests, you can execute::
135135

136-
$ nox -s system-3.7
136+
$ nox -s system-3.8
137137
$ nox -s system-2.7
138138

139139
.. note::
140140

141141
System tests are only configured to run under Python 2.7 and
142-
Python 3.7. For expediency, we do not run them in older versions
142+
Python 3.8. For expediency, we do not run them in older versions
143143
of Python 3.
144144

145145
This alone will not run the tests. You'll need to change some local

google/cloud/storage/blob.py

+5
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,11 @@ def _extract_headers_from_download(self, response):
858858
self.cache_control = response.headers.get("Cache-Control", None)
859859
self.storage_class = response.headers.get("X-Goog-Storage-Class", None)
860860
self.content_language = response.headers.get("Content-Language", None)
861+
self._properties["etag"] = response.headers.get("ETag", None)
862+
self._properties["generation"] = response.headers.get("X-goog-generation", None)
863+
self._properties["metageneration"] = response.headers.get(
864+
"X-goog-metageneration", None
865+
)
861866
# 'X-Goog-Hash': 'crc32c=4gcgLQ==,md5=CS9tHYTtyFntzj7B9nkkJQ==',
862867
x_goog_hash = response.headers.get("X-Goog-Hash", "")
863868

tests/system/test_blob.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -173,26 +173,38 @@ def test_blob_crud_w_user_project(
173173
# Exercise 'objects.insert' w/ userProject.
174174
blob.upload_from_filename(info["path"])
175175
gen0 = blob.generation
176+
etag0 = blob.etag
176177

177178
# Upload a second generation of the blob
178179
blob.upload_from_string(gen1_payload)
179180
gen1 = blob.generation
181+
etag1 = blob.etag
180182

181183
blob0 = with_user_project.blob("SmallFile", generation=gen0)
182184
blob1 = with_user_project.blob("SmallFile", generation=gen1)
183185

184186
# Exercise 'objects.get' w/ generation
185-
assert with_user_project.get_blob(blob.name).generation == gen1
186-
assert with_user_project.get_blob(blob.name, generation=gen0).generation == gen0
187+
blob1 = with_user_project.get_blob(blob.name)
188+
assert blob1.generation == gen1
189+
assert blob1.etag == etag1
190+
blob0 = with_user_project.get_blob(blob.name, generation=gen0)
191+
assert blob0.generation == gen0
192+
assert blob0.etag == etag0
187193

188194
try:
189195
# Exercise 'objects.get' (metadata) w/ userProject.
190196
assert blob.exists()
191197
blob.reload()
192198

193199
# Exercise 'objects.get' (media) w/ userProject.
200+
blob0 = with_user_project.blob("SmallFile", generation=gen0)
201+
blob1 = with_user_project.blob("SmallFile", generation=gen1)
202+
assert blob0.etag is None
203+
assert blob1.etag is None
194204
assert blob0.download_as_bytes() == gen0_payload
195205
assert blob1.download_as_bytes() == gen1_payload
206+
assert blob0.etag == etag0
207+
assert blob1.etag == etag1
196208

197209
# Exercise 'objects.patch' w/ userProject.
198210
blob0.content_language = "en"
@@ -468,10 +480,14 @@ def test_blob_download_as_text(
468480
blob = shared_bucket.blob("MyBuffer")
469481
payload = "Hello World"
470482
blob.upload_from_string(payload)
483+
etag = blob.etag
471484
blobs_to_delete.append(blob)
472485

486+
blob = shared_bucket.blob("MyBuffer")
487+
assert blob.etag is None
473488
stored_contents = blob.download_as_text()
474489
assert stored_contents == payload
490+
assert blob.etag == etag
475491

476492

477493
def test_blob_upload_w_gzip_encoded_download_raw(

tests/unit/test_blob.py

+12
Original file line numberDiff line numberDiff line change
@@ -1035,8 +1035,11 @@ def test__extract_headers_from_download_gzipped(self):
10351035
"Content-Language": "ko-kr",
10361036
"Cache-Control": "max-age=1337;public",
10371037
"Content-Encoding": "gzip",
1038+
"Etag": "kittens",
10381039
"X-Goog-Storage-Class": "STANDARD",
10391040
"X-Goog-Hash": "crc32c=4gcgLQ==,md5=CS9tHYTtyFntzj7B9nkkJQ==",
1041+
"X-goog-generation": 42,
1042+
"X-goog-metageneration": 4,
10401043
},
10411044
# { "x": 5 } gzipped
10421045
content=b"\x1f\x8b\x08\x00\xcfo\x17_\x02\xff\xabVP\xaaP\xb2R0U\xa8\x05\x00\xa1\xcaQ\x93\n\x00\x00\x00",
@@ -1050,6 +1053,9 @@ def test__extract_headers_from_download_gzipped(self):
10501053
self.assertEqual(blob.storage_class, "STANDARD")
10511054
self.assertEqual(blob.md5_hash, "CS9tHYTtyFntzj7B9nkkJQ==")
10521055
self.assertEqual(blob.crc32c, "4gcgLQ==")
1056+
self.assertEqual(blob.etag, "kittens")
1057+
self.assertEqual(blob.generation, 42)
1058+
self.assertEqual(blob.metageneration, 4)
10531059

10541060
def test__extract_headers_from_download_empty(self):
10551061
blob_name = "blob-name"
@@ -1064,8 +1070,11 @@ def test__extract_headers_from_download_empty(self):
10641070
"Content-Language": "en-US",
10651071
"Cache-Control": "max-age=1337;public",
10661072
"Content-Encoding": "gzip",
1073+
"Etag": "kittens",
10671074
"X-Goog-Storage-Class": "STANDARD",
10681075
"X-Goog-Hash": "crc32c=4/c+LQ==,md5=CS9tHYTt/+ntzj7B9nkkJQ==",
1076+
"X-goog-generation": 42,
1077+
"X-goog-metageneration": 4,
10691078
},
10701079
content=b"",
10711080
)
@@ -1074,6 +1083,9 @@ def test__extract_headers_from_download_empty(self):
10741083
self.assertEqual(blob.content_language, "en-US")
10751084
self.assertEqual(blob.md5_hash, "CS9tHYTt/+ntzj7B9nkkJQ==")
10761085
self.assertEqual(blob.crc32c, "4/c+LQ==")
1086+
self.assertEqual(blob.etag, "kittens")
1087+
self.assertEqual(blob.generation, 42)
1088+
self.assertEqual(blob.metageneration, 4)
10771089

10781090
def test__extract_headers_from_download_w_hash_response_header_none(self):
10791091
blob_name = "blob-name"

0 commit comments

Comments
 (0)