Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/boto/s3/connection.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
1 # Copyright (c) 2006-2012 Mitch Garnaat http://garnaat.org/ | |
2 # Copyright (c) 2012 Amazon.com, Inc. or its affiliates. | |
3 # Copyright (c) 2010, Eucalyptus Systems, Inc. | |
4 # All rights reserved. | |
5 # | |
6 # Permission is hereby granted, free of charge, to any person obtaining a | |
7 # copy of this software and associated documentation files (the | |
8 # "Software"), to deal in the Software without restriction, including | |
9 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
10 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
11 # persons to whom the Software is furnished to do so, subject to the fol- | |
12 # lowing conditions: | |
13 # | |
14 # The above copyright notice and this permission notice shall be included | |
15 # in all copies or substantial portions of the Software. | |
16 # | |
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
18 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
19 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
20 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
21 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
23 # IN THE SOFTWARE. | |
24 | |
25 import xml.sax | |
26 import base64 | |
27 from boto.compat import six, urllib | |
28 import time | |
29 | |
30 from boto.auth import detect_potential_s3sigv4 | |
31 import boto.utils | |
32 from boto.connection import AWSAuthConnection | |
33 from boto import handler | |
34 from boto.s3.bucket import Bucket | |
35 from boto.s3.key import Key | |
36 from boto.resultset import ResultSet | |
37 from boto.exception import BotoClientError, S3ResponseError | |
38 | |
39 | |
40 def check_lowercase_bucketname(n): | |
41 """ | |
42 Bucket names must not contain uppercase characters. We check for | |
43 this by appending a lowercase character and testing with islower(). | |
44 Note this also covers cases like numeric bucket names with dashes. | |
45 | |
46 >>> check_lowercase_bucketname("Aaaa") | |
47 Traceback (most recent call last): | |
48 ... | |
49 BotoClientError: S3Error: Bucket names cannot contain upper-case | |
50 characters when using either the sub-domain or virtual hosting calling | |
51 format. | |
52 | |
53 >>> check_lowercase_bucketname("1234-5678-9123") | |
54 True | |
55 >>> check_lowercase_bucketname("abcdefg1234") | |
56 True | |
57 """ | |
58 if not (n + 'a').islower(): | |
59 raise BotoClientError("Bucket names cannot contain upper-case " \ | |
60 "characters when using either the sub-domain or virtual " \ | |
61 "hosting calling format.") | |
62 return True | |
63 | |
64 | |
65 def assert_case_insensitive(f): | |
66 def wrapper(*args, **kwargs): | |
67 if len(args) == 3 and check_lowercase_bucketname(args[2]): | |
68 pass | |
69 return f(*args, **kwargs) | |
70 return wrapper | |
71 | |
72 | |
73 class _CallingFormat(object): | |
74 | |
75 def get_bucket_server(self, server, bucket): | |
76 return '' | |
77 | |
78 def build_url_base(self, connection, protocol, server, bucket, key=''): | |
79 url_base = '%s://' % protocol | |
80 url_base += self.build_host(server, bucket) | |
81 url_base += connection.get_path(self.build_path_base(bucket, key)) | |
82 return url_base | |
83 | |
84 def build_host(self, server, bucket): | |
85 if bucket == '': | |
86 return server | |
87 else: | |
88 return self.get_bucket_server(server, bucket) | |
89 | |
90 def build_auth_path(self, bucket, key=''): | |
91 key = boto.utils.get_utf8_value(key) | |
92 path = '' | |
93 if bucket != '': | |
94 path = '/' + bucket | |
95 return path + '/%s' % urllib.parse.quote(key) | |
96 | |
97 def build_path_base(self, bucket, key=''): | |
98 key = boto.utils.get_utf8_value(key) | |
99 return '/%s' % urllib.parse.quote(key) | |
100 | |
101 | |
102 class SubdomainCallingFormat(_CallingFormat): | |
103 | |
104 @assert_case_insensitive | |
105 def get_bucket_server(self, server, bucket): | |
106 return '%s.%s' % (bucket, server) | |
107 | |
108 | |
109 class VHostCallingFormat(_CallingFormat): | |
110 | |
111 @assert_case_insensitive | |
112 def get_bucket_server(self, server, bucket): | |
113 return bucket | |
114 | |
115 | |
116 class OrdinaryCallingFormat(_CallingFormat): | |
117 | |
118 def get_bucket_server(self, server, bucket): | |
119 return server | |
120 | |
121 def build_path_base(self, bucket, key=''): | |
122 key = boto.utils.get_utf8_value(key) | |
123 path_base = '/' | |
124 if bucket: | |
125 path_base += "%s/" % bucket | |
126 return path_base + urllib.parse.quote(key) | |
127 | |
128 | |
129 class ProtocolIndependentOrdinaryCallingFormat(OrdinaryCallingFormat): | |
130 | |
131 def build_url_base(self, connection, protocol, server, bucket, key=''): | |
132 url_base = '//' | |
133 url_base += self.build_host(server, bucket) | |
134 url_base += connection.get_path(self.build_path_base(bucket, key)) | |
135 return url_base | |
136 | |
137 | |
138 class Location(object): | |
139 | |
140 DEFAULT = '' # US Classic Region | |
141 EU = 'EU' # Ireland | |
142 EUCentral1 = 'eu-central-1' # Frankfurt | |
143 USWest = 'us-west-1' | |
144 USWest2 = 'us-west-2' | |
145 SAEast = 'sa-east-1' | |
146 APNortheast = 'ap-northeast-1' | |
147 APSoutheast = 'ap-southeast-1' | |
148 APSoutheast2 = 'ap-southeast-2' | |
149 CNNorth1 = 'cn-north-1' | |
150 | |
151 | |
152 class NoHostProvided(object): | |
153 # An identifying object to help determine whether the user provided a | |
154 # ``host`` or not. Never instantiated. | |
155 pass | |
156 | |
157 | |
158 class HostRequiredError(BotoClientError): | |
159 pass | |
160 | |
161 | |
162 class S3Connection(AWSAuthConnection): | |
163 | |
164 DefaultHost = 's3.amazonaws.com' | |
165 DefaultCallingFormat = boto.config.get('s3', 'calling_format', 'boto.s3.connection.SubdomainCallingFormat') | |
166 QueryString = 'Signature=%s&Expires=%d&AWSAccessKeyId=%s' | |
167 | |
168 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, | |
169 is_secure=True, port=None, proxy=None, proxy_port=None, | |
170 proxy_user=None, proxy_pass=None, | |
171 host=NoHostProvided, debug=0, https_connection_factory=None, | |
172 calling_format=DefaultCallingFormat, path='/', | |
173 provider='aws', bucket_class=Bucket, security_token=None, | |
174 suppress_consec_slashes=True, anon=False, | |
175 validate_certs=None, profile_name=None): | |
176 no_host_provided = False | |
177 # Try falling back to the boto config file's value, if present. | |
178 if host is NoHostProvided: | |
179 host = boto.config.get('s3', 'host') | |
180 if host is None: | |
181 host = self.DefaultHost | |
182 no_host_provided = True | |
183 if isinstance(calling_format, six.string_types): | |
184 calling_format=boto.utils.find_class(calling_format)() | |
185 self.calling_format = calling_format | |
186 self.bucket_class = bucket_class | |
187 self.anon = anon | |
188 super(S3Connection, self).__init__(host, | |
189 aws_access_key_id, aws_secret_access_key, | |
190 is_secure, port, proxy, proxy_port, proxy_user, proxy_pass, | |
191 debug=debug, https_connection_factory=https_connection_factory, | |
192 path=path, provider=provider, security_token=security_token, | |
193 suppress_consec_slashes=suppress_consec_slashes, | |
194 validate_certs=validate_certs, profile_name=profile_name) | |
195 # We need to delay until after the call to ``super`` before checking | |
196 # to see if SigV4 is in use. | |
197 if no_host_provided: | |
198 if 'hmac-v4-s3' in self._required_auth_capability(): | |
199 raise HostRequiredError( | |
200 "When using SigV4, you must specify a 'host' parameter." | |
201 ) | |
202 | |
203 @detect_potential_s3sigv4 | |
204 def _required_auth_capability(self): | |
205 if self.anon: | |
206 return ['anon'] | |
207 else: | |
208 return ['s3'] | |
209 | |
210 def __iter__(self): | |
211 for bucket in self.get_all_buckets(): | |
212 yield bucket | |
213 | |
214 def __contains__(self, bucket_name): | |
215 return not (self.lookup(bucket_name) is None) | |
216 | |
217 def set_bucket_class(self, bucket_class): | |
218 """ | |
219 Set the Bucket class associated with this bucket. By default, this | |
220 would be the boto.s3.key.Bucket class but if you want to subclass that | |
221 for some reason this allows you to associate your new class. | |
222 | |
223 :type bucket_class: class | |
224 :param bucket_class: A subclass of Bucket that can be more specific | |
225 """ | |
226 self.bucket_class = bucket_class | |
227 | |
228 def build_post_policy(self, expiration_time, conditions): | |
229 """ | |
230 Taken from the AWS book Python examples and modified for use with boto | |
231 """ | |
232 assert isinstance(expiration_time, time.struct_time), \ | |
233 'Policy document must include a valid expiration Time object' | |
234 | |
235 # Convert conditions object mappings to condition statements | |
236 | |
237 return '{"expiration": "%s",\n"conditions": [%s]}' % \ | |
238 (time.strftime(boto.utils.ISO8601, expiration_time), ",".join(conditions)) | |
239 | |
240 def build_post_form_args(self, bucket_name, key, expires_in=6000, | |
241 acl=None, success_action_redirect=None, | |
242 max_content_length=None, | |
243 http_method='http', fields=None, | |
244 conditions=None, storage_class='STANDARD', | |
245 server_side_encryption=None): | |
246 """ | |
247 Taken from the AWS book Python examples and modified for use with boto | |
248 This only returns the arguments required for the post form, not the | |
249 actual form. This does not return the file input field which also | |
250 needs to be added | |
251 | |
252 :type bucket_name: string | |
253 :param bucket_name: Bucket to submit to | |
254 | |
255 :type key: string | |
256 :param key: Key name, optionally add ${filename} to the end to | |
257 attach the submitted filename | |
258 | |
259 :type expires_in: integer | |
260 :param expires_in: Time (in seconds) before this expires, defaults | |
261 to 6000 | |
262 | |
263 :type acl: string | |
264 :param acl: A canned ACL. One of: | |
265 * private | |
266 * public-read | |
267 * public-read-write | |
268 * authenticated-read | |
269 * bucket-owner-read | |
270 * bucket-owner-full-control | |
271 | |
272 :type success_action_redirect: string | |
273 :param success_action_redirect: URL to redirect to on success | |
274 | |
275 :type max_content_length: integer | |
276 :param max_content_length: Maximum size for this file | |
277 | |
278 :type http_method: string | |
279 :param http_method: HTTP Method to use, "http" or "https" | |
280 | |
281 :type storage_class: string | |
282 :param storage_class: Storage class to use for storing the object. | |
283 Valid values: STANDARD | REDUCED_REDUNDANCY | |
284 | |
285 :type server_side_encryption: string | |
286 :param server_side_encryption: Specifies server-side encryption | |
287 algorithm to use when Amazon S3 creates an object. | |
288 Valid values: None | AES256 | |
289 | |
290 :rtype: dict | |
291 :return: A dictionary containing field names/values as well as | |
292 a url to POST to | |
293 | |
294 .. code-block:: python | |
295 | |
296 | |
297 """ | |
298 if fields is None: | |
299 fields = [] | |
300 if conditions is None: | |
301 conditions = [] | |
302 expiration = time.gmtime(int(time.time() + expires_in)) | |
303 | |
304 # Generate policy document | |
305 conditions.append('{"bucket": "%s"}' % bucket_name) | |
306 if key.endswith("${filename}"): | |
307 conditions.append('["starts-with", "$key", "%s"]' % key[:-len("${filename}")]) | |
308 else: | |
309 conditions.append('{"key": "%s"}' % key) | |
310 if acl: | |
311 conditions.append('{"acl": "%s"}' % acl) | |
312 fields.append({"name": "acl", "value": acl}) | |
313 if success_action_redirect: | |
314 conditions.append('{"success_action_redirect": "%s"}' % success_action_redirect) | |
315 fields.append({"name": "success_action_redirect", "value": success_action_redirect}) | |
316 if max_content_length: | |
317 conditions.append('["content-length-range", 0, %i]' % max_content_length) | |
318 | |
319 if self.provider.security_token: | |
320 fields.append({'name': 'x-amz-security-token', | |
321 'value': self.provider.security_token}) | |
322 conditions.append('{"x-amz-security-token": "%s"}' % self.provider.security_token) | |
323 | |
324 if storage_class: | |
325 fields.append({'name': 'x-amz-storage-class', | |
326 'value': storage_class}) | |
327 conditions.append('{"x-amz-storage-class": "%s"}' % storage_class) | |
328 | |
329 if server_side_encryption: | |
330 fields.append({'name': 'x-amz-server-side-encryption', | |
331 'value': server_side_encryption}) | |
332 conditions.append('{"x-amz-server-side-encryption": "%s"}' % server_side_encryption) | |
333 | |
334 policy = self.build_post_policy(expiration, conditions) | |
335 | |
336 # Add the base64-encoded policy document as the 'policy' field | |
337 policy_b64 = base64.b64encode(policy) | |
338 fields.append({"name": "policy", "value": policy_b64}) | |
339 | |
340 # Add the AWS access key as the 'AWSAccessKeyId' field | |
341 fields.append({"name": "AWSAccessKeyId", | |
342 "value": self.aws_access_key_id}) | |
343 | |
344 # Add signature for encoded policy document as the | |
345 # 'signature' field | |
346 signature = self._auth_handler.sign_string(policy_b64) | |
347 fields.append({"name": "signature", "value": signature}) | |
348 fields.append({"name": "key", "value": key}) | |
349 | |
350 # HTTPS protocol will be used if the secure HTTP option is enabled. | |
351 url = '%s://%s/' % (http_method, | |
352 self.calling_format.build_host(self.server_name(), | |
353 bucket_name)) | |
354 | |
355 return {"action": url, "fields": fields} | |
356 | |
357 def generate_url_sigv4(self, expires_in, method, bucket='', key='', | |
358 headers=None, force_http=False, | |
359 response_headers=None, version_id=None, | |
360 iso_date=None): | |
361 path = self.calling_format.build_path_base(bucket, key) | |
362 auth_path = self.calling_format.build_auth_path(bucket, key) | |
363 host = self.calling_format.build_host(self.server_name(), bucket) | |
364 | |
365 # For presigned URLs we should ignore the port if it's HTTPS | |
366 if host.endswith(':443'): | |
367 host = host[:-4] | |
368 | |
369 params = {} | |
370 if version_id is not None: | |
371 params['VersionId'] = version_id | |
372 | |
373 if response_headers is not None: | |
374 params.update(response_headers) | |
375 | |
376 http_request = self.build_base_http_request(method, path, auth_path, | |
377 headers=headers, host=host, | |
378 params=params) | |
379 | |
380 return self._auth_handler.presign(http_request, expires_in, | |
381 iso_date=iso_date) | |
382 | |
383 def generate_url(self, expires_in, method, bucket='', key='', headers=None, | |
384 query_auth=True, force_http=False, response_headers=None, | |
385 expires_in_absolute=False, version_id=None): | |
386 if self._auth_handler.capability[0] == 'hmac-v4-s3' and query_auth: | |
387 # Handle the special sigv4 case | |
388 return self.generate_url_sigv4(expires_in, method, bucket=bucket, | |
389 key=key, headers=headers, force_http=force_http, | |
390 response_headers=response_headers, version_id=version_id) | |
391 | |
392 headers = headers or {} | |
393 if expires_in_absolute: | |
394 expires = int(expires_in) | |
395 else: | |
396 expires = int(time.time() + expires_in) | |
397 auth_path = self.calling_format.build_auth_path(bucket, key) | |
398 auth_path = self.get_path(auth_path) | |
399 # optional version_id and response_headers need to be added to | |
400 # the query param list. | |
401 extra_qp = [] | |
402 if version_id is not None: | |
403 extra_qp.append("versionId=%s" % version_id) | |
404 if response_headers: | |
405 for k, v in response_headers.items(): | |
406 extra_qp.append("%s=%s" % (k, urllib.parse.quote(v))) | |
407 if self.provider.security_token: | |
408 headers['x-amz-security-token'] = self.provider.security_token | |
409 if extra_qp: | |
410 delimiter = '?' if '?' not in auth_path else '&' | |
411 auth_path += delimiter + '&'.join(extra_qp) | |
412 self.calling_format.build_path_base(bucket, key) | |
413 if query_auth and not self.anon: | |
414 c_string = boto.utils.canonical_string(method, auth_path, headers, | |
415 expires, self.provider) | |
416 b64_hmac = self._auth_handler.sign_string(c_string) | |
417 encoded_canonical = urllib.parse.quote(b64_hmac, safe='') | |
418 query_part = '?' + self.QueryString % (encoded_canonical, expires, | |
419 self.aws_access_key_id) | |
420 else: | |
421 query_part = '' | |
422 if headers: | |
423 hdr_prefix = self.provider.header_prefix | |
424 for k, v in headers.items(): | |
425 if k.startswith(hdr_prefix): | |
426 # headers used for sig generation must be | |
427 # included in the url also. | |
428 extra_qp.append("%s=%s" % (k, urllib.parse.quote(v))) | |
429 if extra_qp: | |
430 delimiter = '?' if not query_part else '&' | |
431 query_part += delimiter + '&'.join(extra_qp) | |
432 if force_http: | |
433 protocol = 'http' | |
434 port = 80 | |
435 else: | |
436 protocol = self.protocol | |
437 port = self.port | |
438 return self.calling_format.build_url_base(self, protocol, | |
439 self.server_name(port), | |
440 bucket, key) + query_part | |
441 | |
442 def get_all_buckets(self, headers=None): | |
443 response = self.make_request('GET', headers=headers) | |
444 body = response.read() | |
445 if response.status > 300: | |
446 raise self.provider.storage_response_error( | |
447 response.status, response.reason, body) | |
448 rs = ResultSet([('Bucket', self.bucket_class)]) | |
449 h = handler.XmlHandler(rs, self) | |
450 if not isinstance(body, bytes): | |
451 body = body.encode('utf-8') | |
452 xml.sax.parseString(body, h) | |
453 return rs | |
454 | |
455 def get_canonical_user_id(self, headers=None): | |
456 """ | |
457 Convenience method that returns the "CanonicalUserID" of the | |
458 user who's credentials are associated with the connection. | |
459 The only way to get this value is to do a GET request on the | |
460 service which returns all buckets associated with the account. | |
461 As part of that response, the canonical userid is returned. | |
462 This method simply does all of that and then returns just the | |
463 user id. | |
464 | |
465 :rtype: string | |
466 :return: A string containing the canonical user id. | |
467 """ | |
468 rs = self.get_all_buckets(headers=headers) | |
469 return rs.owner.id | |
470 | |
471 def get_bucket(self, bucket_name, validate=True, headers=None): | |
472 """ | |
473 Retrieves a bucket by name. | |
474 | |
475 If the bucket does not exist, an ``S3ResponseError`` will be raised. If | |
476 you are unsure if the bucket exists or not, you can use the | |
477 ``S3Connection.lookup`` method, which will either return a valid bucket | |
478 or ``None``. | |
479 | |
480 If ``validate=False`` is passed, no request is made to the service (no | |
481 charge/communication delay). This is only safe to do if you are **sure** | |
482 the bucket exists. | |
483 | |
484 If the default ``validate=True`` is passed, a request is made to the | |
485 service to ensure the bucket exists. Prior to Boto v2.25.0, this fetched | |
486 a list of keys (but with a max limit set to ``0``, always returning an empty | |
487 list) in the bucket (& included better error messages), at an | |
488 increased expense. As of Boto v2.25.0, this now performs a HEAD request | |
489 (less expensive but worse error messages). | |
490 | |
491 If you were relying on parsing the error message before, you should call | |
492 something like:: | |
493 | |
494 bucket = conn.get_bucket('<bucket_name>', validate=False) | |
495 bucket.get_all_keys(maxkeys=0) | |
496 | |
497 :type bucket_name: string | |
498 :param bucket_name: The name of the bucket | |
499 | |
500 :type headers: dict | |
501 :param headers: Additional headers to pass along with the request to | |
502 AWS. | |
503 | |
504 :type validate: boolean | |
505 :param validate: If ``True``, it will try to verify the bucket exists | |
506 on the service-side. (Default: ``True``) | |
507 """ | |
508 if validate: | |
509 return self.head_bucket(bucket_name, headers=headers) | |
510 else: | |
511 return self.bucket_class(self, bucket_name) | |
512 | |
513 def head_bucket(self, bucket_name, headers=None): | |
514 """ | |
515 Determines if a bucket exists by name. | |
516 | |
517 If the bucket does not exist, an ``S3ResponseError`` will be raised. | |
518 | |
519 :type bucket_name: string | |
520 :param bucket_name: The name of the bucket | |
521 | |
522 :type headers: dict | |
523 :param headers: Additional headers to pass along with the request to | |
524 AWS. | |
525 | |
526 :returns: A <Bucket> object | |
527 """ | |
528 response = self.make_request('HEAD', bucket_name, headers=headers) | |
529 body = response.read() | |
530 if response.status == 200: | |
531 return self.bucket_class(self, bucket_name) | |
532 elif response.status == 403: | |
533 # For backward-compatibility, we'll populate part of the exception | |
534 # with the most-common default. | |
535 err = self.provider.storage_response_error( | |
536 response.status, | |
537 response.reason, | |
538 body | |
539 ) | |
540 err.error_code = 'AccessDenied' | |
541 err.error_message = 'Access Denied' | |
542 raise err | |
543 elif response.status == 404: | |
544 # For backward-compatibility, we'll populate part of the exception | |
545 # with the most-common default. | |
546 err = self.provider.storage_response_error( | |
547 response.status, | |
548 response.reason, | |
549 body | |
550 ) | |
551 err.error_code = 'NoSuchBucket' | |
552 err.error_message = 'The specified bucket does not exist' | |
553 raise err | |
554 else: | |
555 raise self.provider.storage_response_error( | |
556 response.status, response.reason, body) | |
557 | |
558 def lookup(self, bucket_name, validate=True, headers=None): | |
559 """ | |
560 Attempts to get a bucket from S3. | |
561 | |
562 Works identically to ``S3Connection.get_bucket``, save for that it | |
563 will return ``None`` if the bucket does not exist instead of throwing | |
564 an exception. | |
565 | |
566 :type bucket_name: string | |
567 :param bucket_name: The name of the bucket | |
568 | |
569 :type headers: dict | |
570 :param headers: Additional headers to pass along with the request to | |
571 AWS. | |
572 | |
573 :type validate: boolean | |
574 :param validate: If ``True``, it will try to fetch all keys within the | |
575 given bucket. (Default: ``True``) | |
576 """ | |
577 try: | |
578 bucket = self.get_bucket(bucket_name, validate, headers=headers) | |
579 except: | |
580 bucket = None | |
581 return bucket | |
582 | |
583 def create_bucket(self, bucket_name, headers=None, | |
584 location=Location.DEFAULT, policy=None): | |
585 """ | |
586 Creates a new located bucket. By default it's in the USA. You can pass | |
587 Location.EU to create a European bucket (S3) or European Union bucket | |
588 (GCS). | |
589 | |
590 :type bucket_name: string | |
591 :param bucket_name: The name of the new bucket | |
592 | |
593 :type headers: dict | |
594 :param headers: Additional headers to pass along with the request to AWS. | |
595 | |
596 :type location: str | |
597 :param location: The location of the new bucket. You can use one of the | |
598 constants in :class:`boto.s3.connection.Location` (e.g. Location.EU, | |
599 Location.USWest, etc.). | |
600 | |
601 :type policy: :class:`boto.s3.acl.CannedACLStrings` | |
602 :param policy: A canned ACL policy that will be applied to the | |
603 new key in S3. | |
604 | |
605 """ | |
606 check_lowercase_bucketname(bucket_name) | |
607 | |
608 if policy: | |
609 if headers: | |
610 headers[self.provider.acl_header] = policy | |
611 else: | |
612 headers = {self.provider.acl_header: policy} | |
613 if location == Location.DEFAULT: | |
614 data = '' | |
615 else: | |
616 data = '<CreateBucketConfiguration><LocationConstraint>' + \ | |
617 location + '</LocationConstraint></CreateBucketConfiguration>' | |
618 response = self.make_request('PUT', bucket_name, headers=headers, | |
619 data=data) | |
620 body = response.read() | |
621 if response.status == 409: | |
622 raise self.provider.storage_create_error( | |
623 response.status, response.reason, body) | |
624 if response.status == 200: | |
625 return self.bucket_class(self, bucket_name) | |
626 else: | |
627 raise self.provider.storage_response_error( | |
628 response.status, response.reason, body) | |
629 | |
630 def delete_bucket(self, bucket, headers=None): | |
631 """ | |
632 Removes an S3 bucket. | |
633 | |
634 In order to remove the bucket, it must first be empty. If the bucket is | |
635 not empty, an ``S3ResponseError`` will be raised. | |
636 | |
637 :type bucket_name: string | |
638 :param bucket_name: The name of the bucket | |
639 | |
640 :type headers: dict | |
641 :param headers: Additional headers to pass along with the request to | |
642 AWS. | |
643 """ | |
644 response = self.make_request('DELETE', bucket, headers=headers) | |
645 body = response.read() | |
646 if response.status != 204: | |
647 raise self.provider.storage_response_error( | |
648 response.status, response.reason, body) | |
649 | |
650 def make_request(self, method, bucket='', key='', headers=None, data='', | |
651 query_args=None, sender=None, override_num_retries=None, | |
652 retry_handler=None): | |
653 if isinstance(bucket, self.bucket_class): | |
654 bucket = bucket.name | |
655 if isinstance(key, Key): | |
656 key = key.name | |
657 path = self.calling_format.build_path_base(bucket, key) | |
658 boto.log.debug('path=%s' % path) | |
659 auth_path = self.calling_format.build_auth_path(bucket, key) | |
660 boto.log.debug('auth_path=%s' % auth_path) | |
661 host = self.calling_format.build_host(self.server_name(), bucket) | |
662 if query_args: | |
663 path += '?' + query_args | |
664 boto.log.debug('path=%s' % path) | |
665 auth_path += '?' + query_args | |
666 boto.log.debug('auth_path=%s' % auth_path) | |
667 return super(S3Connection, self).make_request( | |
668 method, path, headers, | |
669 data, host, auth_path, sender, | |
670 override_num_retries=override_num_retries, | |
671 retry_handler=retry_handler | |
672 ) |