1 from StringIO import StringIO
2 from s3.errors import S3Error
3 from s3.parsers import parseListKeys
4
6 """
7 S3Object class
8 """
9 - def __init__(self, key, data, metadata, last_modified=None, bucket=None):
10 self.key = key
11 self.data = data
12 self.metadata = {}
13 for key in metadata:
14 self.metadata[key.lower()] = metadata[key]
15 self.last_modified = last_modified
16 self._bucket = bucket
17
19 """
20 Save a modified (or same) S3Object to the bucket associated with it.
21 """
22 if self._bucket:
23 self._bucket.save(self)
24 else:
25 raise S3Error('No bucket selected', 'Object has no bucket associated with itself', self.key)
26
28 """
29 Deletes an S3Object from the bucket.
30 """
31 if self._bucket:
32 self._bucket.delete(self)
33 else:
34 raise S3Error('No bucket selected', 'Object has no bucket associated with itself', self.key)
35
36
38 """
39 Returns a dictionary of metadata keys prepared for inserting them into headers.
40 """
41 headers = {}
42 for key in self.metadata:
43 headers['x-amz-meta-' + key] = self.metadata[key]
44 return headers
45
46
48 """
49 S3Bucket class
50
51 Behaves like a dictionary of keys in the bucket, with some additional methods.
52 """
53
57
58 - def _request(self, method='', obj=None, send_io=None, params=None, headers=None, *args):
59 return getattr(self._s3_conn.clone(), method)(self.name, obj, send_io=send_io,
60 params=params, headers=headers,
61 *args)
62
64 return self.name
65
67 return self.name
68
69
70 - def get(self, key, headers={}):
71 """
72 Get an S3Object from the bucket.
73
74 @param key: Key of the object
75 @type key: string
76 @param headers: Dictionary of additional headers
77 @type headers: dict
78 @return: Selected S3Object if found or None
79 @rtype: S3Object
80 """
81 response = self._request('GET', key, headers=headers)
82 headers = response.getheaders()
83 data = response.read()
84 metadata = {}
85 last_modified = ''
86 for header in headers:
87 if header[0].startswith('x-amz-meta-'):
88 metadata[header[0][11:]] = header[1]
89 elif header[0] == 'Last-Modified':
90 last_modified = header[1]
91 return S3Object(key, data, metadata, last_modified, self)
92
93
94 - def head(self, key, headers={}):
95 """
96 Get an object's headers from bucket.
97
98 @param key: Key of the object
99 @type key: string
100 @param headers: Dictionary of additional headers
101 @type headers: dict
102 @return: Dictionary of object headers
103 @rtype: dict
104 """
105 return dict(self._request('HEAD', key, headers=headers).getheaders())
106
107
108 - def list(self, prefix=None, marker=None, max_keys=None, delimiter=None):
109 """
110 List bucket objects.
111
112 @param prefix: Limits the response to keys which begin with the
113 indicated prefix. You can use prefixes to separate a
114 bucket into different sets of keys in a way similar to
115 how a file system uses folders.
116 @type prefix: string
117 @param marker: Indicates where in the bucket to begin listing.
118 The list will only include keys that occur
119 lexicographically after marker. This is convenient for
120 pagination: To get the next page of results use the
121 last key of the current page as the marker.
122 @type marker: string
123 @param max_keys: The maximum number of keys you'd like to see in the
124 response body. The server may return fewer than this
125 many keys, but will not return more.
126 @type max_keys: int
127 @param delimiter: Causes keys that contain the same string between the
128 prefix and the first occurrence of the delimiter to be
129 rolled up into a single result element in the
130 CommonPrefixes collection. These rolled-up keys are
131 not returned elsewhere in the response.
132 @type delimiter: char
133 """
134 keys = self.keys(prefix=prefix, marker=marker, max_keys=max_keys, delimiter=delimiter)
135 objects = []
136 for key in keys:
137 objects.append(self.get(key))
138 return objects
139
140
141 - def save(self, s3object, headers={}):
142 """
143 Save an S3Object into bucket.
144
145 @param s3object: An S3Object that has to be saved
146 @type s3object: S3Object
147 @param headers: Dictionary of additional headers
148 @type headers: dict
149 """
150 data = s3object.data
151 if isinstance(data, str) or isinstance(data, unicode):
152 data = StringIO(data)
153 for key in s3object.metadata:
154 headers['x-amz-meta-' + key] = s3object.metadata[key]
155 self._request('PUT', s3object.key, send_io=data, headers=headers)
156
157
159 """
160 Delete an S3Object, a key or list of keys or objects from bucket.
161
162 @param objects: S3Object, S3Object key, or list of both to be deleted
163 @type objects: S3Object, string, list of S3Objects or list of strings
164 """
165 if not isinstance(objects, list):
166 objects = [objects,]
167 for obj in objects:
168 if isinstance(obj, S3Object):
169 self._request('DELETE', obj.key)
170 else:
171 self._request('DELETE', obj)
172
173
174 - def keys(self, prefix=None, marker=None, max_keys=None, delimiter=None):
175 """
176 Returns a flat list of object keys in bucket.
177
178 @param prefix: Limits the response to keys which begin with the
179 indicated prefix. You can use prefixes to separate a
180 bucket into different sets of keys in a way similar to
181 how a file system uses folders.
182 @type prefix: string
183 @param marker: Indicates where in the bucket to begin listing.
184 The list will only include keys that occur
185 lexicographically after marker. This is convenient for
186 pagination: To get the next page of results use the
187 last key of the current page as the marker.
188 @type marker: string
189 @param max_keys: The maximum number of keys you'd like to see in the
190 response body. The server may return fewer than this
191 many keys, but will not return more.
192 @type max_keys: int
193 @param delimiter: Causes keys that contain the same string between the
194 prefix and the first occurrence of the delimiter to be
195 rolled up into a single result element in the
196 CommonPrefixes collection. These rolled-up keys are
197 not returned elsewhere in the response.
198 @type delimiter: char
199 """
200 params = {}
201 if prefix: params['prefix'] = prefix
202 if marker: params['marker'] = marker
203 if max_keys: params['max-keys'] = max_keys
204 if delimiter: params['delimiter'] = delimiter
205
206 response = self._request('GET', params=params)
207 return parseListKeys(response.read())
208
209 values = list
210
212 """
213 Returns (key, value) pairs, where, keys are object names, and values are S3Objects.
214
215 @return: List of (bucket name, bucket) pairs
216 @rtype: list
217 """
218 return zip(self.keys(), self.list())
219
221 """
222 Does key exists in bucket.
223 """
224 return key in self.keys(prefix=key)
225
226
228 """
229 Get an S3Object from bucket.
230 """
231 return self.get(key)
232
233
235 """
236 Delete a key from bucket.
237 """
238 self.delete(s3object)
239