Merge ~barryprice/spade/+git/spade:black into spade:master
- Git
- lp:~barryprice/spade/+git/spade
- black
- Merge into master
Proposed by
Barry Price
Status: | Needs review |
---|---|
Proposed branch: | ~barryprice/spade/+git/spade:black |
Merge into: | spade:master |
Prerequisite: | ~barryprice/spade/+git/spade:flake8 |
Diff against target: |
905 lines (+201/-191) 10 files modified
Makefile (+2/-1) spade/AzureBucket.py (+61/-63) spade/BucketObject.py (+2/-3) spade/GCEBucket.py (+14/-33) spade/LocalBucket.py (+15/-3) spade/ObjectIO.py (+5/-8) spade/S3Bucket.py (+61/-43) spade/Spade.py (+14/-8) spade/StdioBucket.py (+1/-1) spade/SwiftBucket.py (+26/-28) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Haw Loeung | +1 | Approve | |
Review via email: mp+394573@code.launchpad.net |
This proposal supersedes a proposal from 2020-11-27.
Commit message
Reformatted with black
Description of the change
To post a comment you must log in.
Unmerged commits
- 1734965... by Barry Price
-
Merge branch 'flake8' into black
- 6817bc5... by Barry Price
-
Merge branch 'master' into flake8
- c85283a... by Barry Price
-
Typo
- a86afa5... by Barry Price
-
Reformatted with black
- aa2d273... by Barry Price
-
Address all Flake8 errors bar complexity (noqa: C901)
- 7e34f18... by Barry Price
-
Modernise, switch to python3 exclusively
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/Makefile b/Makefile |
2 | index 03544a4..ceb744e 100644 |
3 | --- a/Makefile |
4 | +++ b/Makefile |
5 | @@ -2,7 +2,8 @@ version := $(shell python3 -c "from spade import __version__; print(__version__) |
6 | tempfiles_snap := snap/snapcraft.yaml spade*.snap |
7 | |
8 | blacken: |
9 | - @echo "Normalising python layout with black (but not really)." |
10 | + @echo "Normalising python layout with black" |
11 | + @tox -e black |
12 | |
13 | lint: blacken |
14 | @echo "Running flake8" |
15 | diff --git a/spade/AzureBucket.py b/spade/AzureBucket.py |
16 | index 0f7a62d..e04830a 100644 |
17 | --- a/spade/AzureBucket.py |
18 | +++ b/spade/AzureBucket.py |
19 | @@ -26,15 +26,14 @@ from spade.ObjectIO import ObjectIO |
20 | |
21 | DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
22 | DEFAULT_DELIMITER = "/" |
23 | -AZURE_SEGMENTSIZE = 1024 * 1024 * 4 # Azure block (segment) limit = 4MB |
24 | +AZURE_SEGMENTSIZE = 1024 * 1024 * 4 # Azure block (segment) limit = 4MB |
25 | MAX_AZURE_OBJECTSIZE = 64 * 1024 * 1024 # Azure block blob limit = 64MB |
26 | USER_META_PREFIX = "x-ms-meta-" |
27 | |
28 | |
29 | -class AzureBucket(): |
30 | +class AzureBucket: |
31 | def __init__(self, creds): |
32 | - wanted = ["private_key", |
33 | - "storage_account"] |
34 | + wanted = ["private_key", "storage_account"] |
35 | for k in wanted: |
36 | if k not in creds: |
37 | # Missing creds |
38 | @@ -67,9 +66,19 @@ class AzureBucket(): |
39 | def _create_azure_auth_signature(self, bucket, headers, query, method): |
40 | # Note: auth_headers are in a specific order |
41 | # see https://msdn.microsoft.com/en-gb/library/azure/dd179428.aspx |
42 | - auth_headers = ("Content-Encoding", "Content-Language", "Content-Length", "Content-MD5", |
43 | - "Content-Type", "Date", "If-Modified-Since", "If-Match", "If-None-Match", |
44 | - "If-Unmodified-Since", "Range") |
45 | + auth_headers = ( |
46 | + "Content-Encoding", |
47 | + "Content-Language", |
48 | + "Content-Length", |
49 | + "Content-MD5", |
50 | + "Content-Type", |
51 | + "Date", |
52 | + "If-Modified-Since", |
53 | + "If-Match", |
54 | + "If-None-Match", |
55 | + "If-Unmodified-Since", |
56 | + "Range", |
57 | + ) |
58 | canonicalized_headers = self._dict_to_str(headers, strip_final=False, startswith="x-ms-") |
59 | query_string = self._dict_to_str(query) |
60 | |
61 | @@ -84,9 +93,11 @@ class AzureBucket(): |
62 | signature += canonicalized_headers |
63 | signature += canonicalized_resource |
64 | |
65 | - encoded_sig = base64.b64encode(hmac.new(base64.b64decode(self.primaryKey), |
66 | - msg=signature.encode("utf-8"), |
67 | - digestmod=hashlib.sha256).digest()) |
68 | + encoded_sig = base64.b64encode( |
69 | + hmac.new( |
70 | + base64.b64decode(self.primaryKey), msg=signature.encode("utf-8"), digestmod=hashlib.sha256 |
71 | + ).digest() |
72 | + ) |
73 | return encoded_sig.decode('utf-8') |
74 | |
75 | # Requests documented here: https://msdn.microsoft.com/en-GB/library/azure/dd135733.aspx |
76 | @@ -104,10 +115,9 @@ class AzureBucket(): |
77 | headers["Authorization"] = "SharedKey {}:{}".format(self.storageAccount, auth) |
78 | |
79 | query_string = self._dict_to_str(query, kv_sep="=", entry_sep="&") |
80 | - response, content = h.request(uri=self.blobUri + bucket + "?" + query_string, |
81 | - method=method, |
82 | - body=body, |
83 | - headers=headers) |
84 | + response, content = h.request( |
85 | + uri=self.blobUri + bucket + "?" + query_string, method=method, body=body, headers=headers |
86 | + ) |
87 | return response, content |
88 | |
89 | def listBuckets(self): |
90 | @@ -153,9 +163,7 @@ class AzureBucket(): |
91 | |
92 | def getBucketMetadata(self, bucketName): |
93 | # Standard metadata |
94 | - response, content = self._send_azure_request(bucketName, |
95 | - query={"restype": "container"}, |
96 | - method="HEAD") |
97 | + response, content = self._send_azure_request(bucketName, query={"restype": "container"}, method="HEAD") |
98 | if response.get("status") == "200": |
99 | co = BucketObject(bucketName) |
100 | co.size = int(response.get("content-length", 0)) |
101 | @@ -169,9 +177,7 @@ class AzureBucket(): |
102 | |
103 | # User-defined metadata |
104 | query = {"restype": "container", "comp": "metadata"} |
105 | - response, content = self._send_azure_request(bucketName, |
106 | - query=query, |
107 | - method="HEAD") |
108 | + response, content = self._send_azure_request(bucketName, query=query, method="HEAD") |
109 | if response.get("status") == "200": |
110 | for k, v in response.items(): |
111 | if k.lower().startswith(USER_META_PREFIX): |
112 | @@ -187,8 +193,7 @@ class AzureBucket(): |
113 | |
114 | def getObjectMetadata(self, bucketName, objectName): |
115 | # Standard metadata |
116 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
117 | - method="HEAD") |
118 | + response, content = self._send_azure_request(bucketName + "/" + objectName, method="HEAD") |
119 | if response.get("status") == "200": |
120 | co = BucketObject(objectName) |
121 | co.size = int(response.get("content-length", 0)) |
122 | @@ -201,9 +206,9 @@ class AzureBucket(): |
123 | raise BucketError("Azure Failed to find object metadata") |
124 | |
125 | # User-defined metadata |
126 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
127 | - query={"comp": "metadata"}, |
128 | - method="HEAD") |
129 | + response, content = self._send_azure_request( |
130 | + bucketName + "/" + objectName, query={"comp": "metadata"}, method="HEAD" |
131 | + ) |
132 | if response.get("status") == "200": |
133 | for k, v in response.items(): |
134 | if k.lower().startswith(USER_META_PREFIX): |
135 | @@ -225,9 +230,13 @@ class AzureBucket(): |
136 | return metadata.rawMetadata.get("etag", "") |
137 | |
138 | def setObjectMetadata(self, bucketName, objectName, metadata, overwrite=False): |
139 | - systemMetadata = ["x-ms-blob-cache-control", "x-ms-blob-content-type", |
140 | - "x-ms-blob-content-md5", "x-ms-blob-content-encoding", |
141 | - "x-ms-blob-content-language"] |
142 | + systemMetadata = [ |
143 | + "x-ms-blob-cache-control", |
144 | + "x-ms-blob-content-type", |
145 | + "x-ms-blob-content-md5", |
146 | + "x-ms-blob-content-encoding", |
147 | + "x-ms-blob-content-language", |
148 | + ] |
149 | |
150 | newStandardMetadata = {} |
151 | newUserMetadata = {} |
152 | @@ -252,18 +261,16 @@ class AzureBucket(): |
153 | newUserMetadata[USER_META_PREFIX + k] = v |
154 | |
155 | # Standard metadata |
156 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
157 | - query={"comp": "properties"}, |
158 | - headers=newStandardMetadata, |
159 | - method="PUT") |
160 | + response, content = self._send_azure_request( |
161 | + bucketName + "/" + objectName, query={"comp": "properties"}, headers=newStandardMetadata, method="PUT" |
162 | + ) |
163 | if response.get("status") != "200": |
164 | raise BucketError("Azure Failed to set standard object metadata") |
165 | |
166 | # User metadata |
167 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
168 | - query={"comp": "metadata"}, |
169 | - headers=newUserMetadata, |
170 | - method="PUT") |
171 | + response, content = self._send_azure_request( |
172 | + bucketName + "/" + objectName, query={"comp": "metadata"}, headers=newUserMetadata, method="PUT" |
173 | + ) |
174 | if response.get("status") != "200": |
175 | raise BucketError("Azure Failed to set user object metadata") |
176 | return |
177 | @@ -272,8 +279,7 @@ class AzureBucket(): |
178 | headers = {} |
179 | if rangeStart is not None and rangeEnd is not None: |
180 | headers["Range"] = "bytes={}-{}".format(rangeStart, rangeEnd) |
181 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
182 | - headers=headers) |
183 | + response, content = self._send_azure_request(bucketName + "/" + objectName, headers=headers) |
184 | if response.get("status") == "200" or response.get("status") == "206": |
185 | return content |
186 | elif response.get("status") == "416": |
187 | @@ -293,11 +299,13 @@ class AzureBucket(): |
188 | blockId = base64.b64encode("{:06}".format(blockNumber).encode('utf-8')).decode('utf-8') |
189 | headers = {} |
190 | headers["Content-Length"] = str(len(blockContent)) |
191 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
192 | - query={"comp": "block", "blockid": blockId}, |
193 | - headers=headers, |
194 | - body=blockContent, |
195 | - method="PUT") |
196 | + response, content = self._send_azure_request( |
197 | + bucketName + "/" + objectName, |
198 | + query={"comp": "block", "blockid": blockId}, |
199 | + headers=headers, |
200 | + body=blockContent, |
201 | + method="PUT", |
202 | + ) |
203 | if not response.get("status") == "201": |
204 | raise BucketError("Azure Failed to upload multi-part block") |
205 | blockList.append(blockId) |
206 | @@ -312,11 +320,9 @@ class AzureBucket(): |
207 | xml += "</BlockList>\n" |
208 | headers = {} |
209 | headers["Content-Length"] = str(len(xml)) |
210 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
211 | - query={"comp": "blocklist"}, |
212 | - headers=headers, |
213 | - body=xml, |
214 | - method="PUT") |
215 | + response, content = self._send_azure_request( |
216 | + bucketName + "/" + objectName, query={"comp": "blocklist"}, headers=headers, body=xml, method="PUT" |
217 | + ) |
218 | if not response.get("status") == "201": |
219 | raise BucketError("Azure Failed to upload blocklist") |
220 | return |
221 | @@ -329,10 +335,9 @@ class AzureBucket(): |
222 | headers["Content-Length"] = str(size) |
223 | headers["Content-Type"] = "application/octet-stream" |
224 | headers["x-ms-blob-type"] = "BlockBlob" |
225 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
226 | - headers=headers, |
227 | - body=src, |
228 | - method="PUT") |
229 | + response, content = self._send_azure_request( |
230 | + bucketName + "/" + objectName, headers=headers, body=src, method="PUT" |
231 | + ) |
232 | if response.get("status") == "201": |
233 | return |
234 | else: |
235 | @@ -342,8 +347,7 @@ class AzureBucket(): |
236 | return ObjectIO(self, bucketName, objectName, blockSize) |
237 | |
238 | def deleteObject(self, bucketName, objectName): |
239 | - response, content = self._send_azure_request(bucketName + "/" + objectName, |
240 | - method="DELETE") |
241 | + response, content = self._send_azure_request(bucketName + "/" + objectName, method="DELETE") |
242 | if response.get("status") == "202": |
243 | return |
244 | else: |
245 | @@ -351,9 +355,7 @@ class AzureBucket(): |
246 | |
247 | def createBucket(self, bucketName): |
248 | query = {"restype": "container"} |
249 | - response, content = self._send_azure_request(bucketName, |
250 | - query=query, |
251 | - method="PUT") |
252 | + response, content = self._send_azure_request(bucketName, query=query, method="PUT") |
253 | if response.get("status") == "201": |
254 | return |
255 | else: |
256 | @@ -362,9 +364,7 @@ class AzureBucket(): |
257 | # Note: Azure doesn't care if the container is empty or not |
258 | def deleteBucket(self, bucketName): |
259 | query = {"restype": "container"} |
260 | - response, content = self._send_azure_request(bucketName, |
261 | - query=query, |
262 | - method="DELETE") |
263 | + response, content = self._send_azure_request(bucketName, query=query, method="DELETE") |
264 | if response.get("status") == "202": |
265 | return |
266 | else: |
267 | @@ -378,9 +378,7 @@ class AzureBucket(): |
268 | return [] |
269 | |
270 | query = {"restype": "container", "comp": "acl"} |
271 | - response, content = self._send_azure_request(bucketName, |
272 | - query=query, |
273 | - method="HEAD") |
274 | + response, content = self._send_azure_request(bucketName, query=query, method="HEAD") |
275 | if response.get("status") != "200": |
276 | raise BucketError("Azure Failed to read ACL data") |
277 | |
278 | diff --git a/spade/BucketObject.py b/spade/BucketObject.py |
279 | index 9f8a258..7d35d24 100644 |
280 | --- a/spade/BucketObject.py |
281 | +++ b/spade/BucketObject.py |
282 | @@ -1,5 +1,4 @@ |
283 | -class BucketObject(): |
284 | - |
285 | +class BucketObject: |
286 | class ObjectType: |
287 | File = 0 |
288 | Directory = 1 |
289 | @@ -18,7 +17,7 @@ class BucketError(Exception): |
290 | pass |
291 | |
292 | |
293 | -class BucketAcl(): |
294 | +class BucketAcl: |
295 | def __init__(self): |
296 | self.acls = {} |
297 | |
298 | diff --git a/spade/GCEBucket.py b/spade/GCEBucket.py |
299 | index 2029886..56761f1 100644 |
300 | --- a/spade/GCEBucket.py |
301 | +++ b/spade/GCEBucket.py |
302 | @@ -23,6 +23,7 @@ import datetime |
303 | import base64 |
304 | from apiclient import discovery, errors |
305 | from apiclient.http import MediaIoBaseUpload |
306 | + |
307 | try: |
308 | from oauth2client.service_account import ServiceAccountCredentials |
309 | except ImportError: |
310 | @@ -34,11 +35,9 @@ DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
311 | DEFAULT_DELIMITER = "/" |
312 | |
313 | |
314 | -class GCEBucket(): |
315 | +class GCEBucket: |
316 | def __init__(self, creds): |
317 | - wanted = ["client_email", |
318 | - "private_key", |
319 | - "project"] |
320 | + wanted = ["client_email", "private_key", "project"] |
321 | for k in wanted: |
322 | if k not in creds: |
323 | # Missing creds |
324 | @@ -47,12 +46,9 @@ class GCEBucket(): |
325 | # httplib2.debuglevel = 4 |
326 | cloud_storage_auth_scope = 'https://www.googleapis.com/auth/devstorage.full_control' |
327 | try: |
328 | - c = ServiceAccountCredentials.from_json_keyfile_dict(creds, |
329 | - scopes=cloud_storage_auth_scope) |
330 | + c = ServiceAccountCredentials.from_json_keyfile_dict(creds, scopes=cloud_storage_auth_scope) |
331 | except NameError: |
332 | - c = SignedJwtAssertionCredentials(creds['client_email'], |
333 | - creds['private_key'], |
334 | - cloud_storage_auth_scope) |
335 | + c = SignedJwtAssertionCredentials(creds['client_email'], creds['private_key'], cloud_storage_auth_scope) |
336 | http = c.authorize(httplib2.Http()) |
337 | self.gce = discovery.build('storage', 'v1', http=http) |
338 | self.project = creds["project"] |
339 | @@ -79,10 +75,7 @@ class GCEBucket(): |
340 | def listBucketContents(self, bucketName, path=None, delimiter=DEFAULT_DELIMITER): |
341 | contents = [] |
342 | fields_to_return = 'nextPageToken,prefixes,items' |
343 | - req = self.gce.objects().list(bucket=bucketName, |
344 | - prefix=path, |
345 | - delimiter=delimiter, |
346 | - fields=fields_to_return) |
347 | + req = self.gce.objects().list(bucket=bucketName, prefix=path, delimiter=delimiter, fields=fields_to_return) |
348 | while req: |
349 | try: |
350 | resp = req.execute() |
351 | @@ -106,8 +99,7 @@ class GCEBucket(): |
352 | return contents |
353 | |
354 | def getBucketMetadata(self, bucketName): |
355 | - req = self.gce.buckets().get(bucket=bucketName, |
356 | - projection="full") |
357 | + req = self.gce.buckets().get(bucket=bucketName, projection="full") |
358 | try: |
359 | resp = req.execute() |
360 | except errors.HttpError: |
361 | @@ -126,9 +118,7 @@ class GCEBucket(): |
362 | raise BucketError("GCE Setting bucket metadata is not supported in GCE") |
363 | |
364 | def getObjectMetadata(self, bucketName, objectName): |
365 | - req = self.gce.objects().get(bucket=bucketName, |
366 | - object=objectName, |
367 | - projection="full") |
368 | + req = self.gce.objects().get(bucket=bucketName, object=objectName, projection="full") |
369 | try: |
370 | resp = req.execute() |
371 | except errors.HttpError: |
372 | @@ -157,8 +147,7 @@ class GCEBucket(): |
373 | return hexChecksum |
374 | |
375 | def setObjectMetadata(self, bucketName, objectName, metadata, overwrite=False): |
376 | - systemMetadata = ["cacheControl", "contentDisposition", "contentEncoding", |
377 | - "contentLanguage", "contentType"] |
378 | + systemMetadata = ["cacheControl", "contentDisposition", "contentEncoding", "contentLanguage", "contentType"] |
379 | newMetadata = {} |
380 | m = self.getObjectMetadata(bucketName, objectName) |
381 | # Keep existing acls, system metadata and (optionally) user metadata |
382 | @@ -177,9 +166,7 @@ class GCEBucket(): |
383 | newMetadata[k] = v |
384 | else: |
385 | newMetadata["metadata"][k] = v |
386 | - req = self.gce.objects().update(bucket=bucketName, |
387 | - object=objectName, |
388 | - body=newMetadata) |
389 | + req = self.gce.objects().update(bucket=bucketName, object=objectName, body=newMetadata) |
390 | try: |
391 | req.execute() |
392 | except errors.HttpError: |
393 | @@ -190,22 +177,18 @@ class GCEBucket(): |
394 | headers = {} |
395 | if rangeStart is not None and rangeEnd is not None: |
396 | headers['range'] = "bytes={}-{}".format(rangeStart, rangeEnd) |
397 | - req = self.gce.objects().get_media(bucket=bucketName, |
398 | - object=objectName) |
399 | + req = self.gce.objects().get_media(bucket=bucketName, object=objectName) |
400 | http = req.http |
401 | resp, data = http.request(req.uri, headers=headers) |
402 | if resp.get("status") == "416": |
403 | # Requested range not satisfiable |
404 | data = "" |
405 | elif resp.get("status")[0] != "2": |
406 | - raise(BucketError("GCE Failed to find object")) |
407 | + raise (BucketError("GCE Failed to find object")) |
408 | return data |
409 | |
410 | def putObject(self, bucketName, objectName, src, size, blockSize=DEFAULT_BLOCKSIZE): |
411 | - media = MediaIoBaseUpload(src, |
412 | - mimetype="application/octet-stream", |
413 | - chunksize=blockSize, |
414 | - resumable=True) |
415 | + media = MediaIoBaseUpload(src, mimetype="application/octet-stream", chunksize=blockSize, resumable=True) |
416 | req = self.gce.objects().insert(bucket=bucketName, name=objectName, media_body=media) |
417 | resp = None |
418 | while resp is None: |
419 | @@ -243,9 +226,7 @@ class GCEBucket(): |
420 | return |
421 | |
422 | aclTypes = ["list", "read", "write"] |
423 | - aclTypeMap = {"read": "READER", |
424 | - "write": "WRITER", |
425 | - "list": "READER"} |
426 | + aclTypeMap = {"read": "READER", "write": "WRITER", "list": "READER"} |
427 | |
428 | def translateAcl(self, aclData, aliases): |
429 | """ |
430 | diff --git a/spade/LocalBucket.py b/spade/LocalBucket.py |
431 | index f2293ec..9f3f974 100644 |
432 | --- a/spade/LocalBucket.py |
433 | +++ b/spade/LocalBucket.py |
434 | @@ -10,7 +10,7 @@ DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
435 | DEFAULT_DELIMITER = "/" |
436 | |
437 | |
438 | -class LocalBucket(): |
439 | +class LocalBucket: |
440 | def __init__(self, creds): |
441 | wanted = ["bucket_root"] |
442 | for k in wanted: |
443 | @@ -79,8 +79,20 @@ class LocalBucket(): |
444 | co.date = self.standardTimestamp(stat.st_mtime) |
445 | co.contentType = "text/plain" |
446 | raw = {} |
447 | - for num, name in enumerate(["st_mode", "st_ino", "st_dev", "st_nlink", "st_uid", |
448 | - "st_gid", "st_size", "st_atime", "st_mtime", "st_ctime"]): |
449 | + for num, name in enumerate( |
450 | + [ |
451 | + "st_mode", |
452 | + "st_ino", |
453 | + "st_dev", |
454 | + "st_nlink", |
455 | + "st_uid", |
456 | + "st_gid", |
457 | + "st_size", |
458 | + "st_atime", |
459 | + "st_mtime", |
460 | + "st_ctime", |
461 | + ] |
462 | + ): |
463 | raw[name] = stat[num] |
464 | co.rawMetadata = raw |
465 | return co |
466 | diff --git a/spade/ObjectIO.py b/spade/ObjectIO.py |
467 | index fc2b38f..dfdb804 100644 |
468 | --- a/spade/ObjectIO.py |
469 | +++ b/spade/ObjectIO.py |
470 | @@ -2,7 +2,7 @@ import os |
471 | from spade.BucketObject import BucketError |
472 | |
473 | |
474 | -class ObjectIO(): |
475 | +class ObjectIO: |
476 | def __init__(self, parent, bucketName, objectName, blockSize=-1): |
477 | self.parent = parent |
478 | self.bucketName = bucketName |
479 | @@ -36,10 +36,7 @@ class ObjectIO(): |
480 | if self.closed: |
481 | raise ValueError |
482 | |
483 | - data = self.parent.getObject(self.bucketName, |
484 | - self.objectName, |
485 | - self.pos, |
486 | - min(self.pos + n - 1, self.len)) |
487 | + data = self.parent.getObject(self.bucketName, self.objectName, self.pos, min(self.pos + n - 1, self.len)) |
488 | self.pos = self.pos + len(data) |
489 | return data |
490 | |
491 | @@ -56,19 +53,19 @@ class ObjectIO(): |
492 | while self.readlinePos < len(self.readlineBuffer): |
493 | lineEndPos = self.readlineBuffer.find(b"\n", self.readlinePos) |
494 | if lineEndPos >= 0: |
495 | - line = self.readlineBuffer[self.readlinePos:lineEndPos + 1].decode('utf-8') |
496 | + line = self.readlineBuffer[self.readlinePos : lineEndPos + 1].decode('utf-8') |
497 | self.readlinePos = lineEndPos + 1 |
498 | return line |
499 | else: |
500 | newData = self.read(self.blockSize) |
501 | if not newData: |
502 | return |
503 | - self.readlineBuffer = self.readlineBuffer[self.readlinePos:] + newData |
504 | + self.readlineBuffer = self.readlineBuffer[self.readlinePos :] + newData |
505 | self.readlinePos = 0 |
506 | |
507 | # In case the last line has no newline |
508 | if self.readlinePos < len(self.readlineBuffer): |
509 | - return self.readlineBuffer[self.readlinePos:] |
510 | + return self.readlineBuffer[self.readlinePos :] |
511 | |
512 | def readlines(self): |
513 | while True: |
514 | diff --git a/spade/S3Bucket.py b/spade/S3Bucket.py |
515 | index 6d3036e..67f38fb 100644 |
516 | --- a/spade/S3Bucket.py |
517 | +++ b/spade/S3Bucket.py |
518 | @@ -25,15 +25,13 @@ from spade.ObjectIO import ObjectIO |
519 | DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
520 | DEFAULT_DELIMITER = "/" |
521 | MAX_S3_OBJECTSIZE = 5 * 1024 * 1024 * 1024 # S3 object PUT limit = 5GB |
522 | -S3_SEGMENTSIZE = 1024 * 1024 * 1024 # Use 1GB segments for large object uploads |
523 | +S3_SEGMENTSIZE = 1024 * 1024 * 1024 # Use 1GB segments for large object uploads |
524 | USER_META_PREFIX = "x-amz-meta-" |
525 | |
526 | |
527 | -class S3Bucket(): |
528 | +class S3Bucket: |
529 | def __init__(self, creds): |
530 | - wanted = ["aws_access_key_id", |
531 | - "aws_secret_access_key", |
532 | - "aws_region"] |
533 | + wanted = ["aws_access_key_id", "aws_secret_access_key", "aws_region"] |
534 | for k in wanted: |
535 | if k not in creds: |
536 | # Missing creds |
537 | @@ -44,8 +42,9 @@ class S3Bucket(): |
538 | |
539 | def oldLibraryFixes(self): |
540 | try: |
541 | - self.regions = [{"name": region.name, "endpoint": region.endpoint} |
542 | - for region in boto.regioninfo.get_regions("s3")] |
543 | + self.regions = [ |
544 | + {"name": region.name, "endpoint": region.endpoint} for region in boto.regioninfo.get_regions("s3") |
545 | + ] |
546 | except AttributeError: |
547 | # Old boto library doesn't have this, so we'll use the ones we know about |
548 | self.regions = [ |
549 | @@ -63,9 +62,22 @@ class S3Bucket(): |
550 | {'name': 'us-east-1', 'endpoint': 's3.amazonaws.com'}, |
551 | ] |
552 | |
553 | - self.base_fields = set(['content-length', 'content-language', 'content-disposition', |
554 | - 'content-encoding', 'expires', 'content-md5', 'last-modified', |
555 | - 'etag', 'cache-control', 'date', 'content-type', 'x-robots-tag']) |
556 | + self.base_fields = set( |
557 | + [ |
558 | + 'content-length', |
559 | + 'content-language', |
560 | + 'content-disposition', |
561 | + 'content-encoding', |
562 | + 'expires', |
563 | + 'content-md5', |
564 | + 'last-modified', |
565 | + 'etag', |
566 | + 'cache-control', |
567 | + 'date', |
568 | + 'content-type', |
569 | + 'x-robots-tag', |
570 | + ] |
571 | + ) |
572 | |
573 | def _connect(self): |
574 | self.endpoint = None |
575 | @@ -78,11 +90,13 @@ class S3Bucket(): |
576 | if not self.endpoint: |
577 | raise NotImplementedError |
578 | |
579 | - self.s3 = boto.s3.connection.S3Connection(self.creds['aws_access_key_id'], |
580 | - self.creds['aws_secret_access_key'], |
581 | - is_secure=True, |
582 | - host=self.endpoint, |
583 | - calling_format=OrdinaryCallingFormat()) |
584 | + self.s3 = boto.s3.connection.S3Connection( |
585 | + self.creds['aws_access_key_id'], |
586 | + self.creds['aws_secret_access_key'], |
587 | + is_secure=True, |
588 | + host=self.endpoint, |
589 | + calling_format=OrdinaryCallingFormat(), |
590 | + ) |
591 | |
592 | # Boto/S3 gives a 301 redirect response if we try to access a bucket |
593 | # in another region, so make sure we're in the right one |
594 | @@ -94,7 +108,7 @@ class S3Bucket(): |
595 | |
596 | # Strip off the first section (the current region) |
597 | try: |
598 | - websiteEndpoint = websiteEndpoint[websiteEndpoint.find(".") + 1:] |
599 | + websiteEndpoint = websiteEndpoint[websiteEndpoint.find(".") + 1 :] |
600 | except UnboundLocalError: |
601 | raise BucketError("S3 Failed to find bucket") |
602 | |
603 | @@ -176,10 +190,12 @@ class S3Bucket(): |
604 | return metadata.rawMetadata.get("etag", "").strip('"') |
605 | |
606 | def setObjectMetadata(self, bucketName, objectName, metadata, overwrite=False): |
607 | - systemMetadata = ["x-amz-server-side-encryption", |
608 | - "x-amz-storage-class", |
609 | - "x-amz-website-redirect-location", |
610 | - "x-amz-server-side-encryption-aws-kms-key-id"] |
611 | + systemMetadata = [ |
612 | + "x-amz-server-side-encryption", |
613 | + "x-amz-storage-class", |
614 | + "x-amz-website-redirect-location", |
615 | + "x-amz-server-side-encryption-aws-kms-key-id", |
616 | + ] |
617 | self.confirmRegion(bucketName) |
618 | |
619 | # Metadata to remove (if any) |
620 | @@ -300,19 +316,17 @@ class S3Bucket(): |
621 | return |
622 | |
623 | aclTypes = ["list", "read", "write", "readacl", "writeacl"] |
624 | - aclTypeMap = {"read": "READ", |
625 | - "write": "WRITE", |
626 | - "list": "READ", |
627 | - "readacl": "READ_ACP", |
628 | - "writeacl": "WRITE_ACP"} |
629 | + aclTypeMap = {"read": "READ", "write": "WRITE", "list": "READ", "readacl": "READ_ACP", "writeacl": "WRITE_ACP"} |
630 | |
631 | def createAclData(self, user, type): |
632 | - aclData = {"s3_display_name": None, |
633 | - "s3_id": user, |
634 | - "s3_uri": None, |
635 | - "s3_email_address": None, |
636 | - "s3_type": "CanonicalUser", |
637 | - "s3_permission": self.aclTypeMap[type]} |
638 | + aclData = { |
639 | + "s3_display_name": None, |
640 | + "s3_id": user, |
641 | + "s3_uri": None, |
642 | + "s3_email_address": None, |
643 | + "s3_type": "CanonicalUser", |
644 | + "s3_permission": self.aclTypeMap[type], |
645 | + } |
646 | return aclData |
647 | |
648 | def translateAcl(self, aclData, aliases): |
649 | @@ -343,12 +357,14 @@ class S3Bucket(): |
650 | |
651 | acls = BucketAcl() |
652 | for grant in acl.acl.grants: |
653 | - aclData = {"s3_display_name": grant.display_name, |
654 | - "s3_id": grant.id, |
655 | - "s3_uri": grant.uri, |
656 | - "s3_email_address": grant.email_address, |
657 | - "s3_type": grant.type, |
658 | - "s3_permission": grant.permission} |
659 | + aclData = { |
660 | + "s3_display_name": grant.display_name, |
661 | + "s3_id": grant.id, |
662 | + "s3_uri": grant.uri, |
663 | + "s3_email_address": grant.email_address, |
664 | + "s3_type": grant.type, |
665 | + "s3_permission": grant.permission, |
666 | + } |
667 | if grant.permission in ["READ", "FULL_CONTROL"]: |
668 | if objectName: |
669 | acls.add("read", aclData) |
670 | @@ -392,12 +408,14 @@ class S3Bucket(): |
671 | uniqueAcls.append(user) |
672 | |
673 | for user in uniqueAcls: |
674 | - grant = boto.s3.acl.Grant(permission=user["s3_permission"], |
675 | - type=user["s3_type"], |
676 | - id=user["s3_id"], |
677 | - display_name=user["s3_display_name"], |
678 | - uri=user["s3_uri"], |
679 | - email_address=["s3_email_address"]) |
680 | + grant = boto.s3.acl.Grant( |
681 | + permission=user["s3_permission"], |
682 | + type=user["s3_type"], |
683 | + id=user["s3_id"], |
684 | + display_name=user["s3_display_name"], |
685 | + uri=user["s3_uri"], |
686 | + email_address=["s3_email_address"], |
687 | + ) |
688 | s3acl.acl.add_grant(grant) |
689 | |
690 | try: |
691 | diff --git a/spade/Spade.py b/spade/Spade.py |
692 | index b8f4b12..5ebdfc0 100644 |
693 | --- a/spade/Spade.py |
694 | +++ b/spade/Spade.py |
695 | @@ -6,21 +6,25 @@ import re |
696 | import json |
697 | import fnmatch |
698 | from spade.BucketObject import BucketObject, BucketError |
699 | + |
700 | try: |
701 | from urllib.parse import urlparse |
702 | except ImportError: |
703 | from urlparse import urlparse |
704 | |
705 | import gzip |
706 | + |
707 | compressionMethods = ["gzip"] |
708 | if sys.version_info.major >= 3 and sys.version_info.minor >= 3: |
709 | import bz2 |
710 | + |
711 | compressionMethods.append("bzip2") |
712 | import lzma |
713 | + |
714 | compressionMethods.append("xz") |
715 | else: |
716 | # Define a fake lzma.LZMAError |
717 | - class lzma(): |
718 | + class lzma: |
719 | class LZMAError(Exception): |
720 | pass |
721 | |
722 | @@ -29,7 +33,7 @@ DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
723 | DEFAULT_DELIMITER = "/" |
724 | |
725 | |
726 | -class Result(): |
727 | +class Result: |
728 | def __init__(self): |
729 | self.output = [] |
730 | self.error = None |
731 | @@ -38,7 +42,7 @@ class Result(): |
732 | return "<Result object. output: '{}' error: '{}'>".format(self.output, self.error) |
733 | |
734 | |
735 | -class Bucket(): |
736 | +class Bucket: |
737 | def __init__(self, configFile): |
738 | self.configFile = configFile |
739 | self.config = json.loads(open(configFile, 'r').read()) |
740 | @@ -102,21 +106,27 @@ class Bucket(): |
741 | |
742 | if bucketType == "swift": |
743 | from spade.SwiftBucket import SwiftBucket |
744 | + |
745 | return SwiftBucket(self.creds[scheme]) |
746 | elif bucketType == "s3": |
747 | from spade.S3Bucket import S3Bucket |
748 | + |
749 | return S3Bucket(self.creds[scheme]) |
750 | elif bucketType == "gce": |
751 | from spade.GCEBucket import GCEBucket |
752 | + |
753 | return GCEBucket(self.creds[scheme]) |
754 | elif bucketType == "azure": |
755 | from spade.AzureBucket import AzureBucket |
756 | + |
757 | return AzureBucket(self.creds[scheme]) |
758 | elif bucketType == "local": |
759 | from spade.LocalBucket import LocalBucket |
760 | + |
761 | return LocalBucket(self.creds[scheme]) |
762 | elif bucketType == "stdio": |
763 | from spade.StdioBucket import StdioBucket |
764 | + |
765 | return StdioBucket(self.creds[scheme]) |
766 | else: |
767 | raise NotImplementedError |
768 | @@ -449,11 +459,7 @@ class Bucket(): |
769 | ret.error = e |
770 | yield ret |
771 | try: |
772 | - dstBucket.putObject(dstUrl.netloc, |
773 | - dstObjName, |
774 | - fSrc, |
775 | - objMetadata.size, |
776 | - blockSize=args.blockSize) |
777 | + dstBucket.putObject(dstUrl.netloc, dstObjName, fSrc, objMetadata.size, blockSize=args.blockSize) |
778 | except BucketError as e: |
779 | ret.error = e |
780 | fSrc.close() |
781 | diff --git a/spade/StdioBucket.py b/spade/StdioBucket.py |
782 | index ef6cb43..31ba1f6 100644 |
783 | --- a/spade/StdioBucket.py |
784 | +++ b/spade/StdioBucket.py |
785 | @@ -8,7 +8,7 @@ from spade.ObjectIO import ObjectIO |
786 | DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
787 | |
788 | |
789 | -class StdioBucket(): |
790 | +class StdioBucket: |
791 | def __init__(self, creds): |
792 | return |
793 | |
794 | diff --git a/spade/SwiftBucket.py b/spade/SwiftBucket.py |
795 | index 2125b85..0c3adb0 100644 |
796 | --- a/spade/SwiftBucket.py |
797 | +++ b/spade/SwiftBucket.py |
798 | @@ -23,19 +23,15 @@ from spade.ObjectIO import ObjectIO |
799 | |
800 | DEFAULT_BLOCKSIZE = 1024 * 1024 * 10 |
801 | DEFAULT_DELIMITER = "/" |
802 | -MAX_SWIFT_OBJECTSIZE = 5 * 1024 * 1024 * 1024 # Swift object limit = 5GB |
803 | -SWIFT_SEGMENTSIZE = 1024 * 1024 * 1024 # Use 1GB segments for large object uploads |
804 | +MAX_SWIFT_OBJECTSIZE = 5 * 1024 * 1024 * 1024 # Swift object limit = 5GB |
805 | +SWIFT_SEGMENTSIZE = 1024 * 1024 * 1024 # Use 1GB segments for large object uploads |
806 | USER_META_PREFIX = "x-object-meta-" |
807 | ACL_META_PREFIX = "x-container-" |
808 | |
809 | |
810 | -class SwiftBucket(): |
811 | +class SwiftBucket: |
812 | def __init__(self, creds): |
813 | - wanted = ["os_username", |
814 | - "os_password", |
815 | - "os_tenant_name", |
816 | - "os_auth_url", |
817 | - "os_region_name"] |
818 | + wanted = ["os_username", "os_password", "os_tenant_name", "os_auth_url", "os_region_name"] |
819 | for k in wanted: |
820 | if k not in creds: |
821 | # Missing creds |
822 | @@ -51,7 +47,8 @@ class SwiftBucket(): |
823 | key=creds["os_password"], |
824 | retries=5, |
825 | auth_version="2.0", |
826 | - os_options=options) |
827 | + os_options=options, |
828 | + ) |
829 | |
830 | def standardTimestamp(self, timestamp): |
831 | try: |
832 | @@ -109,8 +106,7 @@ class SwiftBucket(): |
833 | return co |
834 | |
835 | def setBucketMetadata(self, bucketName, metadata, overwrite=False): # noqa: C901 |
836 | - systemMetadata = ["content-type", "x-container-bytes-used", |
837 | - "x-container-object-count:", "x-timestamp"] |
838 | + systemMetadata = ["content-type", "x-container-bytes-used", "x-container-object-count:", "x-timestamp"] |
839 | newMetadata = {} |
840 | # Keep existing system metadata and (optionally) user metadata |
841 | m = self.getBucketMetadata(bucketName) |
842 | @@ -163,8 +159,14 @@ class SwiftBucket(): |
843 | return metadata.rawMetadata.get("etag", "").strip('"') |
844 | |
845 | def setObjectMetadata(self, bucketName, objectName, metadata, overwrite=False): |
846 | - systemMetadata = ["content-type", "content-encoding", "content-disposition", |
847 | - "x-delete-at", "x-delete-after", "etag"] |
848 | + systemMetadata = [ |
849 | + "content-type", |
850 | + "content-encoding", |
851 | + "content-disposition", |
852 | + "x-delete-at", |
853 | + "x-delete-after", |
854 | + "etag", |
855 | + ] |
856 | newMetadata = {} |
857 | # Keep existing system metadata and (optionally) user metadata |
858 | m = self.getObjectMetadata(bucketName, objectName) |
859 | @@ -223,11 +225,13 @@ class SwiftBucket(): |
860 | segmentName = '{}/{}/{}/{}/{:08d}'.format(objectName, mtime, size, SWIFT_SEGMENTSIZE, segmentId) |
861 | segmentId += 1 |
862 | try: |
863 | - self.swift.put_object(segmentBucketName, |
864 | - segmentName, |
865 | - src, |
866 | - content_length=segmentSize, |
867 | - content_type="application/octet-stream") |
868 | + self.swift.put_object( |
869 | + segmentBucketName, |
870 | + segmentName, |
871 | + src, |
872 | + content_length=segmentSize, |
873 | + content_type="application/octet-stream", |
874 | + ) |
875 | except swiftclient.ClientException: |
876 | raise BucketError("Swift Failed to put large object segment") |
877 | bytesRead += segmentSize |
878 | @@ -239,12 +243,9 @@ class SwiftBucket(): |
879 | 'x-object-meta-mtime': mtime, |
880 | } |
881 | try: |
882 | - self.swift.put_object(bucketName, |
883 | - objectName, |
884 | - '', |
885 | - content_length=0, |
886 | - content_type="application/octet-stream", |
887 | - headers=headers) |
888 | + self.swift.put_object( |
889 | + bucketName, objectName, '', content_length=0, content_type="application/octet-stream", headers=headers |
890 | + ) |
891 | except swiftclient.ClientException: |
892 | raise BucketError("Swift Failed to put large object manifest") |
893 | return |
894 | @@ -254,10 +255,7 @@ class SwiftBucket(): |
895 | return self.putLargeObject(bucketName, objectName, src, size, min(blockSize, SWIFT_SEGMENTSIZE)) |
896 | |
897 | try: |
898 | - self.swift.put_object(bucketName, |
899 | - objectName, |
900 | - src, |
901 | - content_type="application/octet-stream") |
902 | + self.swift.put_object(bucketName, objectName, src, content_type="application/octet-stream") |
903 | except swiftclient.exceptions.ClientException: |
904 | raise BucketError("Swift Failed to put object") |
905 | return |
LGTM