winazurestorageのフォーク
修訂 | c1d6f9c7e1c128422ec584def3ab81937a02c4f7 (tree) |
---|---|
時間 | 2012-04-19 22:58:09 |
作者 | hylom <hylom@hylo...> |
Commiter | hylom |
add some lacking table APIs
@@ -150,7 +150,125 @@ class Storage(object): | ||
150 | 150 | else: |
151 | 151 | return "http://%s.%s" % (self._account, self._host) |
152 | 152 | |
153 | -class TableEntity(object): pass | |
153 | +class TableEntityException(Exception): | |
154 | + def __init__(self, value): | |
155 | + self.value = value | |
156 | + def __str__(self): | |
157 | + return repr(self.value) | |
158 | + | |
159 | +class TableEntity(object): | |
160 | + "Table Entity" | |
161 | + def __init__(self, partition_key, row_key, props): | |
162 | + self.partition_key = partition_key | |
163 | + self.row_key = row_key | |
164 | + self.properties = props | |
165 | + | |
166 | +# class Binary(object): | |
167 | +# pass | |
168 | + | |
169 | +# class Guid(object): | |
170 | +# pass | |
171 | + | |
172 | + class Boolean(int): | |
173 | + def __str__(self): | |
174 | + if self: | |
175 | + return "true" | |
176 | + else: | |
177 | + return "false" | |
178 | + | |
179 | + def __repr__(self): | |
180 | + props = ",".join([ k + ":" + str(self.properties[k]) for k in self.properties]) | |
181 | + return ",".join((self.partition_key, self.row_key, props)) | |
182 | + | |
183 | + def add_property(self, key, value): | |
184 | + self.properties[key] = value | |
185 | + | |
186 | + def to_insert_xml(self): | |
187 | + contents = [self._make_property_node(propname, self.properties[propname]) for propname in self.properties] | |
188 | + contents_str = "\n".join(contents) | |
189 | + now_str = datetime.utcnow().isoformat() | |
190 | + xml = """<?xml version="1.0" encoding="utf-8" standalone="yes"?> | |
191 | +<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom"> | |
192 | + <title /> | |
193 | + <author> | |
194 | + <name /> | |
195 | + </author> | |
196 | + <id /> | |
197 | + <content type="application/xml"> | |
198 | + <m:properties> | |
199 | +%(contents)s | |
200 | + <d:PartitionKey>%(partition_key)s</d:PartitionKey> | |
201 | + <d:RowKey>%(row_key)s</d:RowKey> | |
202 | + <d:Timestamp m:type="Edm.DateTime">0001-01-01T00:00:00</d:Timestamp> | |
203 | + </m:properties> | |
204 | + </content> | |
205 | +</entry> | |
206 | +""" % dict(contents=contents_str, now=now_str, partition_key=self.partition_key, row_key=self.row_key) | |
207 | + if isinstance(xml, unicode): | |
208 | + xml = xml.encode('utf-8') | |
209 | + return xml | |
210 | + | |
211 | + def to_update_xml(self): | |
212 | + contents = [self._make_property_node(propname, self.properties[propname]) for propname in self.properties] | |
213 | + contents_str = "\n".join(contents) | |
214 | + now_str = datetime.utcnow().isoformat() + "Z" | |
215 | + xml = """<?xml version="1.0" encoding="utf-8" standalone="yes"?> | |
216 | +<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom"> | |
217 | + <title /> | |
218 | + <updated>%(now)s</updated> | |
219 | + <author> | |
220 | + <name /> | |
221 | + </author> | |
222 | + <id /> | |
223 | + <content type="application/xml"> | |
224 | + <m:properties> | |
225 | +%(contents)s | |
226 | + <d:PartitionKey>%(partition_key)s</d:PartitionKey> | |
227 | + <d:RowKey>%(row_key)s</d:RowKey> | |
228 | + <d:Timestamp m:type="Edm.DateTime">0001-01-01T00:00:00</d:Timestamp> | |
229 | + </m:properties> | |
230 | + </content> | |
231 | +</entry> | |
232 | +""" % dict(contents=contents_str, now=now_str, partition_key=self.partition_key, row_key=self.row_key) | |
233 | + if isinstance(xml, unicode): | |
234 | + xml = xml.encode('utf-8') | |
235 | + return xml | |
236 | + | |
237 | + def _make_property_node(self, name, value): | |
238 | + type = "" | |
239 | + string_repr = "" | |
240 | +# if isinstance(value, TableEntity.Binary): | |
241 | +# type = "Edm.Binary" | |
242 | +# string_repr = str(TableEntity.Binary(value)) | |
243 | + if isinstance(value, bool): | |
244 | + type = "Edm.Boolean" | |
245 | + string_repr = str(TableEntity.Boolean(value)) | |
246 | + elif isinstance(value, datetime): | |
247 | + type = "Edm.DateTime" | |
248 | + string_repr = value.isoformat() | |
249 | + elif isinstance(value, float): | |
250 | + type = "Edm.Double" | |
251 | + string_repr = str(value) | |
252 | +# elif isinstance(value, TableEntity.Guid): | |
253 | +# type = "Edm.Guid" | |
254 | +# string_repr = str(TableEntity.Boolean(value)) | |
255 | + elif isinstance(value, long): | |
256 | + type = "Edm.Int64" | |
257 | + string_repr = str(value) | |
258 | + elif isinstance(value, int): | |
259 | + type = "Edm.Int32" | |
260 | + string_repr = str(value) | |
261 | + elif isinstance(value, str): | |
262 | + type = "Edm.String" | |
263 | + string_repr = value | |
264 | + elif isinstance(value, unicode): | |
265 | + type = "Edm.String" | |
266 | + string_repr = value | |
267 | + if type is not "": | |
268 | + prop_element = """<d:%(name)s m:type="%(type)s">%(value)s</d:%(name)s>""" | |
269 | + return prop_element % dict(name=name, type=type, value=string_repr) | |
270 | + else: | |
271 | + raise TableEntityException("Unexpected property: %s" % (value,)) | |
154 | 272 | |
155 | 273 | class QueueMessage(): pass |
156 | 274 |
@@ -300,6 +418,97 @@ class TableStorage(Storage): | ||
300 | 418 | dom.unlink() |
301 | 419 | return entities |
302 | 420 | |
421 | + def insert_entity(self, table_name, entity): | |
422 | + data = entity.to_insert_xml() | |
423 | + url = "%s/%s" % (self.get_base_url(), table_name) | |
424 | + req = winazurestorage.RequestWithMethod("POST", url, data=data) | |
425 | + req.add_header("Content-Length", "%d" % len(data)) | |
426 | + req.add_header("Content-Type", "application/atom+xml") | |
427 | + self._credentials.sign_table_request(req) | |
428 | + try: | |
429 | + response = urlopen(req) | |
430 | + return response.code | |
431 | + except URLError, e: | |
432 | + return e.code | |
433 | + | |
434 | + def update_entity(self, table_name, partition_key, row_key, entity): | |
435 | + data = entity.to_update_xml() | |
436 | + url = """%s/%s(PartitionKey='%s',RowKey='%s')""" % (self.get_base_url(), table_name, partition_key, row_key) | |
437 | + | |
438 | + req = winazurestorage.RequestWithMethod("PUT", url, data=data) | |
439 | + req.add_header("Content-Length", "%d" % len(data)) | |
440 | + req.add_header("Content-Type", "application/atom+xml") | |
441 | + self._credentials.sign_table_request(req) | |
442 | + try: | |
443 | + response = urlopen(req) | |
444 | + return response.code | |
445 | + except URLError, e: | |
446 | + print data | |
447 | + return e.code | |
448 | + | |
449 | + def merge_entity(self, table_name, partition_key, row_key, entity): | |
450 | + data = entity.to_update_xml() | |
451 | + url = """%s/%s(PartitionKey='%s',RowKey='%s')""" % (self.get_base_url(), table_name, partition_key, row_key) | |
452 | + req = winazurestorage.RequestWithMethod("MERGE", url, data=data) | |
453 | + req.add_header("Content-Length", "%d" % len(data)) | |
454 | + req.add_header("Content-Type", "application/atom+xml") | |
455 | + self._credentials.sign_table_request(req) | |
456 | + try: | |
457 | + response = urlopen(req) | |
458 | + return response.code | |
459 | + except URLError, e: | |
460 | + return e.code | |
461 | + | |
462 | + def delete_entity(self, table_name, partition_key, row_key, condition="*"): | |
463 | + data = "" | |
464 | + url = """%s/%s(PartitionKey='%s',RowKey='%s')""" % (self.get_base_url(), table_name, partition_key, row_key) | |
465 | + req = winazurestorage.RequestWithMethod("DELETE", url, data) | |
466 | + req.add_header("Content-Length", "%d" % len(data)) | |
467 | + req.add_header("Content-Type", "application/atom+xml") | |
468 | + req.add_header("If-Match", condition) | |
469 | + self._credentials.sign_table_request(req) | |
470 | + try: | |
471 | + response = urlopen(req) | |
472 | + return response.code | |
473 | + except URLError, e: | |
474 | + return e.code | |
475 | + | |
476 | + def query_entity(self, table_name, filter): | |
477 | + quoted_filter = urllib.quote(filter) | |
478 | + url = """%s/%s()?$filter=%s""" % (self.get_base_url(), table_name, quoted_filter) | |
479 | + req = winazurestorage.RequestWithMethod("GET", url) | |
480 | + self._credentials.sign_table_request(req) | |
481 | + try: | |
482 | + resp = urlopen(req) | |
483 | + except URLError, e: | |
484 | + return e.code | |
485 | + | |
486 | + dom = minidom.parseString(resp.read()) | |
487 | + entries = dom.getElementsByTagName("entry") | |
488 | + entities = [] | |
489 | + for entry in entries: | |
490 | + entities.append(self._parse_entity(entry)) | |
491 | + dom.unlink() | |
492 | + return entities | |
493 | + | |
494 | + def top_entity(self, table_name, size): | |
495 | + url = """%s/%s()?$top=%s""" % (self.get_base_url(), table_name, size) | |
496 | + req = winazurestorage.RequestWithMethod("GET", url) | |
497 | + self._credentials.sign_table_request(req) | |
498 | + | |
499 | + try: | |
500 | + resp = urlopen(req) | |
501 | + except URLError, e: | |
502 | + return e.code | |
503 | + | |
504 | + dom = minidom.parseString(resp.read()) | |
505 | + entries = dom.getElementsByTagName("entry") | |
506 | + entities = [] | |
507 | + for entry in entries: | |
508 | + entities.append(self._parse_entity(entry)) | |
509 | + dom.unlink() | |
510 | + return entities | |
511 | + | |
303 | 512 | class BlobStorage(Storage): |
304 | 513 | def __init__(self, host = DEVSTORE_BLOB_HOST, account_name = DEVSTORE_ACCOUNT, secret_key = DEVSTORE_SECRET_KEY, use_path_style_uris = None): |
305 | 514 | super(BlobStorage, self).__init__(host, account_name, secret_key, use_path_style_uris) |
@@ -416,3 +625,4 @@ def main(): | ||
416 | 625 | |
417 | 626 | if __name__ == '__main__': |
418 | 627 | main() |
628 | + |