14a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair# Copyright 2015 The Chromium Authors. All rights reserved. 24a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair# Use of this source code is governed by a BSD-style license that can be 34a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair# found in the LICENSE file. 44a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 5edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair"""A module for storing and getting objects from datastore. 64a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 7edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan SinclairThis module provides Get, Set and Delete functions for storing pickleable 8edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairobjects in datastore, with support for large objects greater than 1 MB. 9edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 10edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan SinclairAlthough this module contains ndb.Model classes, these are not intended 11edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairto be used directly by other modules. 12edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 13edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan SinclairApp Engine datastore limits entity size to less than 1 MB; this module 14edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairsupports storing larger objects by splitting the data and using multiple 15edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairdatastore entities and multiple memcache keys. Using ndb.get and pickle, a 16edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclaircomplex data structure can be retrieved more quickly than datastore fetch. 174a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 184a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan SinclairExample: 194a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair john = Account() 204a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair john.username = 'John' 214a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair john.userid = 123 22edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair stored_object.Set(john.userid, john) 234a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair""" 244a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 254a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairimport cPickle as pickle 264a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairimport logging 274a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 284a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairfrom google.appengine.api import memcache 294a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairfrom google.appengine.ext import ndb 304a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 314a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair_MULTIPART_ENTITY_MEMCACHE_KEY = 'multipart_entity_' 324a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 33edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# Maximum number of entities and memcache to save a value. 34edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# The limit for data stored in one datastore entity is 1 MB, 35edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# and the limit for memcache batch operations is 32 MB. See: 36edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair# https://cloud.google.com/appengine/docs/python/memcache/#Python_Limits 374a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair_MAX_NUM_PARTS = 16 384a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 394a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair# Max bytes per entity or value cached with memcache. 404a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair_CHUNK_SIZE = 1000 * 1000 414a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 424a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 43edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairdef Get(key): 44edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Gets the value. 45edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 46edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair Args: 47edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key: String key value. 48edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 49edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair Returns: 50edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair A value for key. 51edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """ 52edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair results = MultipartCache.Get(key) 53edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair if not results: 54edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair results = _GetValueFromDatastore(key) 55edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair MultipartCache.Set(key, results) 56edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair return results 57edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 58edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 59edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairdef Set(key, value): 60edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Sets the value in datastore and memcache with limit of '_MAX_NUM_PARTS' MB. 61edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 62edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair Args: 63edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair key: String key value. 64edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair value: A pickleable value to be stored limited at '_MAX_NUM_PARTS' MB. 65edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """ 66edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair entity = ndb.Key(MultipartEntity, key).get() 67edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair if not entity: 68edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair entity = MultipartEntity(id=key) 69edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair entity.SetData(value) 70edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair entity.Save() 71edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair MultipartCache.Set(key, value) 72edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 73edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 74edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclairdef Delete(key): 75edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair """Deletes the value in datastore and memcache.""" 76edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair ndb.Key(MultipartEntity, key).delete() 77edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair MultipartCache.Delete(key) 78edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 79edfe2194ee8a857cc1e78b4e4020f9b5e7210029Dan Sinclair 804a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairclass MultipartEntity(ndb.Model): 814a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Container for PartEntity.""" 824a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 834a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair # Number of entities use to store serialized. 844a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair size = ndb.IntegerProperty(default=0, indexed=False) 854a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 864a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 874a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def _post_get_hook(cls, key, future): # pylint: disable=unused-argument 884a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Deserializes data from multiple PartEntity.""" 894a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair entity = future.get_result() 904a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if entity is None or not entity.size: 914a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return 924a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 934a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair string_id = entity.key.string_id() 944a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair part_keys = [ndb.Key(MultipartEntity, string_id, PartEntity, i + 1) 954a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for i in xrange(entity.size)] 964a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair part_entities = ndb.get_multi(part_keys) 974a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized = ''.join(p.value for p in part_entities if p is not None) 984a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair entity.SetData(pickle.loads(serialized)) 994a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1004a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1014a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def _pre_delete_hook(cls, key): 1024a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Deletes PartEntity entities.""" 1034a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair part_keys = PartEntity.query(ancestor=key).fetch(keys_only=True) 1044a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair ndb.delete_multi(part_keys) 1054a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1064a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def Save(self): 1074a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Stores serialized data over multiple PartEntity.""" 1084a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized_parts = _Serialize(self.GetData()) 1094a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if len(serialized_parts) > _MAX_NUM_PARTS: 1104a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair logging.error('Max number of parts reached.') 1114a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return 1124a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair part_list = [] 1134a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair num_parts = len(serialized_parts) 1144a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for i in xrange(num_parts): 1154a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if serialized_parts[i] is not None: 116cef7893435aa41160dd1255c43cb8498279738ccChris Craik part = PartEntity(id=i + 1, parent=self.key, value=serialized_parts[i]) 1174a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair part_list.append(part) 1184a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair self.size = num_parts 1194a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair ndb.put_multi(part_list + [self]) 1204a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1214a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def GetData(self): 1224a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return getattr(self, '_data', None) 1234a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1244a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def SetData(self, data): 1254a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair setattr(self, '_data', data) 1264a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1274a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1284a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairclass PartEntity(ndb.Model): 1294a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Holds a part of serialized data for MultipartEntity. 1304a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1314a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair This entity key has the form: 1324a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair ndb.Key('MultipartEntity', multipart_entity_id, 'PartEntity', part_index) 1334a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """ 1344a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair value = ndb.BlobProperty() 1354a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1364a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1374a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairclass MultipartCache(object): 1384a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Contains operations for storing values over multiple memcache keys. 1394a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1404a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair Values are serialized, split, and stored over multiple memcache keys. The 1414a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair head cache stores the expected size. 1424a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """ 1434a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1444a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1454a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def Get(cls, key): 1464a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Gets value in memcache.""" 1474a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair keys = cls._GetCacheKeyList(key) 1484a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair head_key = cls._GetCacheKey(key) 1494a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair cache_values = memcache.get_multi(keys) 1504a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair # Whether we have all the memcache values. 1514a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if len(keys) != len(cache_values) or head_key not in cache_values: 1524a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return None 1534a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1544a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized = '' 1554a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair cache_size = cache_values[head_key] 1564a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair keys.remove(head_key) 1574a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for key in keys[:cache_size]: 1584a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if key not in cache_values: 1594a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return None 1604a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if cache_values[key] is not None: 1614a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized += cache_values[key] 1624a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return pickle.loads(serialized) 1634a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1644a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1654a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def Set(cls, key, value): 1664a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Sets a value in memcache.""" 1674a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized_parts = _Serialize(value) 1684a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if len(serialized_parts) > _MAX_NUM_PARTS: 1694a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair logging.error('Max number of parts reached.') 1704a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return 1714a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1724a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair cached_values = {} 1734a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair cached_values[cls._GetCacheKey(key)] = len(serialized_parts) 1744a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for i in xrange(len(serialized_parts)): 1754a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair cached_values[cls._GetCacheKey(key, i)] = serialized_parts[i] 1764a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair memcache.set_multi(cached_values) 1774a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1784a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1794a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def Delete(cls, key): 1804a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Deletes all cached values for key.""" 1814a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair memcache.delete_multi(cls._GetCacheKeyList(key)) 1824a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1834a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1844a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def _GetCacheKeyList(cls, key): 1854a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Gets a list of head cache key and cache key parts.""" 1864a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair keys = [cls._GetCacheKey(key, i) for i in xrange(_MAX_NUM_PARTS)] 1874a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair keys.append(cls._GetCacheKey(key)) 1884a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return keys 1894a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1904a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair @classmethod 1914a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair def _GetCacheKey(cls, key, index=None): 1924a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Returns either head cache key or cache key part.""" 1934a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if index is not None: 1944a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return _MULTIPART_ENTITY_MEMCACHE_KEY + '%s.%s' % (key, index) 1954a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return _MULTIPART_ENTITY_MEMCACHE_KEY + key 1964a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1974a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 1984a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairdef _GetValueFromDatastore(key): 1994a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair entity = ndb.Key(MultipartEntity, key).get() 2004a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair if not entity: 2014a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return None 2024a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return entity.GetData() 2034a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 2044a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 2054a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclairdef _Serialize(value): 2064a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """Serializes value and returns a list of its parts. 2074a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 2084a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair Args: 2094a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair value: A pickleable value. 2104a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair 2114a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair Returns: 2124a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair A list of string representation of the value that has been pickled and split 2134a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair into _CHUNK_SIZE. 2144a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair """ 2154a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair serialized = pickle.dumps(value, 2) 2164a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair length = len(serialized) 2174a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair values = [] 2184a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for i in xrange(0, length, _CHUNK_SIZE): 219cef7893435aa41160dd1255c43cb8498279738ccChris Craik values.append(serialized[i:i + _CHUNK_SIZE]) 2204a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair for i in xrange(len(values), _MAX_NUM_PARTS): 2214a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair values.append(None) 2224a4f2fe02baf385f6c24fc98c6e17bf6ac5e0724Dan Sinclair return values 223