diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2012-03-26 18:32:22 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2012-03-26 18:32:22 (GMT) |
commit | a80894a68b0d999ad9080f720af95ff7c6880e13 (patch) | |
tree | fc99185f3edde9ccd1fb1af036a1c72dbe9c4998 | |
parent | 92f6ac6ee10b66d6c8162d7e900de6ca142f75d4 (diff) |
Make BLOBs related API more useful
-rw-r--r-- | HACKING | 6 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rwxr-xr-x | examples/client.py | 46 | ||||
-rw-r--r-- | sugar_network/_zerosugar/solution.py | 3 | ||||
-rw-r--r-- | sugar_network/client.py | 93 | ||||
-rw-r--r-- | sugar_network/resources.py | 7 |
6 files changed, 97 insertions, 59 deletions
@@ -13,8 +13,8 @@ Send patches ------------ Create your patches using ``git format`` command and send them to all maintainers from the :doc:`AUTHORS <AUTHORS>` file with CCing to -sugar-network@googlegroups.com. The easiest way it just using -``git send-email`` command. +sugar-network@googlegroups.com. The easiest way is using ``git send-email`` +command. Gitorious forks --------------- @@ -26,4 +26,4 @@ Gitorious forks and request them for merge to the trunk. * http://blog.gitorious.org/2009/07/15/new-merge-request-functionality/ * http://blog.gitorious.org/2009/11/06/awesome-code-review/ -.. _sugar-lint: http://wiki.sugarlabs.org/go/Activity_Team/Sugar_Lint +.. _sugar-lint: http://wiki.sugarlabs.org/go/Platform_Team/Sugar_Lint @@ -1,2 +1,3 @@ - import <bundle_id> data dir into data/ - reuse the same data dir for all SN names for the same context +- treat "/" in user datain requests diff --git a/examples/client.py b/examples/client.py index 25efc52..6888e40 100755 --- a/examples/client.py +++ b/examples/client.py @@ -9,6 +9,7 @@ from cStringIO import StringIO import restful_document import sugar_network as client import sugar_network_server as server +from sugar_network import Context def main(): @@ -17,11 +18,11 @@ def main(): image_url = 'http://sugarlabs.org/assets/logo_black_01.png' print '-- Delete objects' - for i in client.Context.find(): - client.Context.delete(i['guid']) + for i in Context.find(): + Context.delete(i['guid']) def context_new(title): - context = client.Context() + context = Context() context['type'] = 'activity' context['title'] = title context['summary'] = 'Description' @@ -38,39 +39,40 @@ def main(): assert guids[2] and guids[2] != guids[1] and guids[2] != guids[0] print '-- Browse using iterators' - for i, obj in enumerate(client.Context.find()): + for i, obj in enumerate(Context.find()): assert i == obj.offset assert obj['guid'] == guids[i] print '-- Browse by offset' - query = client.Context.find() + query = Context.find() for i in range(query.total): assert query[i]['guid'] == guids[i] print '-- Get objects directly' - assert client.Context(guids[0])['title'] == titles[0] - assert client.Context(guids[1])['title'] == titles[1] - assert client.Context(guids[2])['title'] == titles[2] - - print '-- Set BLOB properties' - with client.Artifact() as artifact: - artifact['context'] = guids[0] - artifact['type'] = 'screenshot' - artifact['title'] = titles[0] - artifact['description'] = titles[0] - artifact.set_blob('preview', StringIO('screenshot-image')) + assert Context(guids[0])['title'] == titles[0] + assert Context(guids[1])['title'] == titles[1] + assert Context(guids[2])['title'] == titles[2] + + print '-- Set BLOB property by stream' + Context(guids[0]).blobs['icon'] = StringIO('stream') + + print '-- Set BLOB property by string' + Context(guids[1]).blobs['icon'] = 'string' print '-- Set BLOB properties by url' - artifact.set_blob_with_url('data', image_url) + Context(guids[2]).blobs['icon'].url = image_url - print '-- Get BLOB properties' + print '-- Get BLOB property by portions' stream = StringIO() - for chunk in client.Artifact(artifact['guid']).get_blob('data'): + for chunk in Context(guids[2]).blobs['icon'].iter_content(): stream.write(chunk) assert stream.getvalue() == urllib2.urlopen(image_url).read() + print '-- Get BLOB property by string' + assert Context(guids[1]).blobs['icon'].content == 'string' + print '-- Query by property value' - for obj in client.Context.find(title='Title2'): + for obj in Context.find(title='Title2'): assert obj['guid'] == guids[1] assert obj['title'] == titles[1] @@ -79,7 +81,7 @@ def main(): time.sleep(3) print '-- Full text search query' - query = client.Context.find(query='Title1 OR Title3') + query = Context.find(query='Title1 OR Title3') assert query.total == 2 assert query[0]['guid'] == guids[0] assert query[0]['title'] == titles[0] @@ -93,7 +95,7 @@ if __name__ == '__main__': server.stats_root.value = 'tmp/stats' server.logdir.value = 'tmp/log' server.index_flush_threshold.value = 1 - server_pid = restful_document.fork(server.resources()) + server_pid = restful_document.fork(server.resources) client.api_url.value = \ 'http://%s:%s' % (server.host.value, server.port.value) diff --git a/sugar_network/_zerosugar/solution.py b/sugar_network/_zerosugar/solution.py index a6cd245..a81a5cd 100644 --- a/sugar_network/_zerosugar/solution.py +++ b/sugar_network/_zerosugar/solution.py @@ -132,7 +132,8 @@ class _Selection(object): if not exists(path): tmp_path = util.TempFilePath(dir=dirname(path)) with file(tmp_path, 'wb') as f: - for chunk in Implementation(self.id).get_blob('bundle'): + impl = Implementation(self.id) + for chunk in impl.blobs['bundle'].iter_content(): f.write(chunk) if not f.tell(): return diff --git a/sugar_network/client.py b/sugar_network/client.py index f9ee8c2..f65f79b 100644 --- a/sugar_network/client.py +++ b/sugar_network/client.py @@ -34,6 +34,10 @@ _logger = logging.getLogger('client') _headers = {} +def delete(resource, guid): + _request('DELETE', [resource, guid]) + + class ServerError(Exception): def __init__(self, request_, error): @@ -191,8 +195,7 @@ class Query(object): if self._reply_properties: params['reply'] = ','.join(self._reply_properties) - reply = request('GET', self._path, params=params, - headers={'Content-Type': 'application/json'}) + reply = _request('GET', self._path, params=params) self._total = reply['total'] result = [None] * len(reply['result']) @@ -225,6 +228,11 @@ class Object(dict): if 'guid' in self: self._path = [resource, self['guid']] + @property + def blobs(self): + enforce(self._path is not None, _('Object needs to be posted first')) + return _Blobs(self._path) + def __enter__(self): return self @@ -235,8 +243,7 @@ class Object(dict): result = self.get(prop) if result is None: if self._path and not self._got: - reply = request('GET', self._path, - headers={'Content-Type': 'application/json'}) + reply = _request('GET', self._path) reply.update(self) self.update(reply) self._got = True @@ -259,7 +266,7 @@ class Object(dict): for i in self._dirty: data[i] = self[i] if 'guid' in self: - request('PUT', self._path, data=data, + _request('PUT', self._path, data=data, headers={'Content-Type': 'application/json'}) else: if 'author' in data: @@ -268,22 +275,65 @@ class Object(dict): else: data['author'] = [sugar.guid()] dict.__setitem__(self, 'author', [sugar.guid()]) - reply = request('POST', [self._resource], data=data, + reply = _request('POST', [self._resource], data=data, headers={'Content-Type': 'application/json'}) self.update(reply) - self._path = '/%s/%s' % (self._resource, self['guid']) + self._path = [self._resource, self['guid']] self._dirty.clear() - def get_blob(self, prop): - enforce('guid' in self, _('Object needs to be posted first')) - response = request('GET', [self._resource, self['guid'], prop], - headers={'Content-Type': 'application/octet-stream'}, - allow_redirects=True) + def call(self, command, method='GET', **kwargs): + enforce(self._path is not None, _('Object needs to be posted first')) + kwargs['cmd'] = command + return _request(method, self._path, params=kwargs) + + +class Blob(object): + + def __init__(self, path): + self._path = path + + @property + def content(self): + """Return entire BLOB value as a string.""" + response = _request('GET', self._path, allow_redirects=True) + if hasattr(response, 'content'): + return response.content + else: + return response + + @property + def path(self): + """Return file-system path to file that contain BLOB value.""" + return '/home/me/Activities/cartoon-builder.activity/' \ + 'activity/activity-cartoonbuilder.svg' + + def iter_content(self): + """Return BLOB value by poritons. + + :returns: + generator that returns BLOB value by chunks + + """ + response = _request('GET', self._path, allow_redirects=True) length = int(response.headers.get('Content-Length', _CHUNK_SIZE)) return response.iter_content(chunk_size=min(length, _CHUNK_SIZE)) - def set_blob(self, prop, data): - enforce('guid' in self, _('Object needs to be posted first')) + def _set_url(self, url): + _request('PUT', self._path, params={'url': url}) + + #: Set BLOB value by url + url = property(None, _set_url) + + +class _Blobs(object): + + def __init__(self, path): + self._path = path + + def __getitem__(self, prop): + return Blob(self._path + [prop]) + + def __setitem__(self, prop, data): headers = None if type(data) is dict: files = data @@ -294,20 +344,11 @@ class Object(dict): else: files = None headers = {'Content-Type': 'application/octet-stream'} - request('PUT', [self._resource, self['guid'], prop], headers=headers, + _request('PUT', self._path + [prop], headers=headers, data=data, files=files) - def set_blob_with_url(self, prop, url): - enforce('guid' in self, _('Object needs to be posted first')) - request('PUT', [self._resource, self['guid'], prop], - params={'url': url}) - - -def delete(resource, guid): - request('DELETE', [resource, guid]) - -def request(method, path, data=None, headers=None, **kwargs): +def _request(method, path, data=None, headers=None, **kwargs): path = '/'.join([i.strip('/') for i in [env.api_url.value] + path]) if not _headers: @@ -357,7 +398,7 @@ def request(method, path, data=None, headers=None, **kwargs): def _register(): - request('POST', ['user'], + _request('POST', ['user'], headers={'Content-Type': 'application/json'}, data={ 'nickname': sugar.nickname() or '', diff --git a/sugar_network/resources.py b/sugar_network/resources.py index 9c2e7bf..c9dc672 100644 --- a/sugar_network/resources.py +++ b/sugar_network/resources.py @@ -50,13 +50,6 @@ class Resource(client.Object): enforce(query.total == 1, _('Found more than one object')) client.Object.__init__(self, self.resource, query[0]) - def call(self, command, **kwargs): - enforce('guid' in self, _('Object needs to be postet first')) - kwargs['cmd'] = command - return client.request('GET', [self.resource, self['guid']], - headers={'Content-Type': 'application/json'}, - params=kwargs) - @classmethod def find(cls, *args, **kwargs): """Query resource objects. |