Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/datastore
diff options
context:
space:
mode:
authorSimon Schampijer <simon@schampijer.de>2010-01-20 10:55:56 (GMT)
committer Simon Schampijer <simon@schampijer.de>2010-01-20 10:55:56 (GMT)
commitaacb5592e538073a4295e075e4228286c6042bcd (patch)
tree1a7a058f59687fd10817b93c3bf9d171bd5d10ac /src/sugar/datastore
parente82073fbd267091f98352cb0a2c1661b8b7c23e9 (diff)
Cleaned up the datastore API
- killed the dbus_helpers, moved that to the datastore.py - added documentation - pep8 and pylint clean
Diffstat (limited to 'src/sugar/datastore')
-rw-r--r--src/sugar/datastore/Makefile.am1
-rw-r--r--src/sugar/datastore/datastore.py282
-rw-r--r--src/sugar/datastore/dbus_helpers.py115
3 files changed, 224 insertions, 174 deletions
diff --git a/src/sugar/datastore/Makefile.am b/src/sugar/datastore/Makefile.am
index a5f16b7..81d760c 100644
--- a/src/sugar/datastore/Makefile.am
+++ b/src/sugar/datastore/Makefile.am
@@ -1,5 +1,4 @@
sugardir = $(pythondir)/sugar/datastore
sugar_PYTHON = \
__init__.py \
- dbus_helpers.py \
datastore.py
diff --git a/src/sugar/datastore/datastore.py b/src/sugar/datastore/datastore.py
index 8d23721..cea2265 100644
--- a/src/sugar/datastore/datastore.py
+++ b/src/sugar/datastore/datastore.py
@@ -1,4 +1,5 @@
# Copyright (C) 2007, One Laptop Per Child
+# Copyright (C) 2010, Simon Schampijer
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -16,7 +17,7 @@
# Boston, MA 02111-1307, USA.
"""
-STABLE.
+STABLE
"""
import logging
@@ -27,64 +28,86 @@ import tempfile
import gobject
import gconf
import gio
+import dbus
+import dbus.glib
from sugar import env
-from sugar.datastore import dbus_helpers
from sugar import mime
+DS_DBUS_SERVICE = "org.laptop.sugar.DataStore"
+DS_DBUS_INTERFACE = "org.laptop.sugar.DataStore"
+DS_DBUS_PATH = "/org/laptop/sugar/DataStore"
+
+_data_store = None
+_data_store_listener = None
+
+
+def _get_data_store():
+ global _data_store
+
+ if not _data_store:
+ _bus = dbus.SessionBus()
+ _data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE,
+ DS_DBUS_PATH),
+ DS_DBUS_INTERFACE)
+ return _data_store
+
class DSMetadata(gobject.GObject):
+ """A representation of the metadata associated with a DS entry."""
__gsignals__ = {
'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
- def __init__(self, props=None):
+ def __init__(self, properties=None):
gobject.GObject.__init__(self)
- if not props:
- self._props = {}
+ if not properties:
+ self._properties = {}
else:
- self._props = props
+ self._properties = properties
default_keys = ['activity', 'activity_id',
'mime_type', 'title_set_by_user']
for key in default_keys:
- if not self._props.has_key(key):
- self._props[key] = ''
+ if key not in self._properties:
+ self._properties[key] = ''
def __getitem__(self, key):
- return self._props[key]
+ return self._properties[key]
def __setitem__(self, key, value):
- if not self._props.has_key(key) or self._props[key] != value:
- self._props[key] = value
+ if key not in self._properties or self._properties[key] != value:
+ self._properties[key] = value
self.emit('updated')
def __delitem__(self, key):
- del self._props[key]
+ del self._properties[key]
def __contains__(self, key):
- return self._props.__contains__(key)
+ return self._properties.__contains__(key)
def has_key(self, key):
- return self._props.has_key(key)
+ logging.warning(".has_key() is deprecated, use 'in'")
+ return key in self._properties
def keys(self):
- return self._props.keys()
+ return self._properties.keys()
def get_dictionary(self):
- return self._props
+ return self._properties
def copy(self):
- return DSMetadata(self._props.copy())
+ return DSMetadata(self._properties.copy())
def get(self, key, default=None):
- if self._props.has_key(key):
- return self._props[key]
+ if key in self._properties:
+ return self._properties[key]
else:
return default
class DSObject(object):
+ """A representation of a DS entry."""
def __init__(self, object_id, metadata=None, file_path=None):
self.object_id = object_id
@@ -95,7 +118,8 @@ class DSObject(object):
def get_metadata(self):
if self._metadata is None and not self.object_id is None:
- metadata = DSMetadata(dbus_helpers.get_properties(self.object_id))
+ properties = _get_data_store().get_properties(self.object_id)
+ metadata = DSMetadata(properties)
self._metadata = metadata
return self._metadata
@@ -107,7 +131,7 @@ class DSObject(object):
def get_file_path(self, fetch=True):
if fetch and self._file_path is None and not self.object_id is None:
- self.set_file_path(dbus_helpers.get_filename(self.object_id))
+ self.set_file_path(_get_data_store().get_filename(self.object_id))
self._owns_file = True
return self._file_path
@@ -141,7 +165,12 @@ class DSObject(object):
def copy(self):
return DSObject(None, self._metadata.copy(), self._file_path)
+
class RawObject(object):
+ """A representation for objects not in the DS but
+ in the file system.
+
+ """
def __init__(self, file_path):
stat = os.stat(file_path)
@@ -198,12 +227,20 @@ class RawObject(object):
def get(object_id):
+ """Get the properties of the object with the ID given.
+
+ Keyword arguments:
+ object_id -- unique identifier of the object
+
+ Return: a DSObject
+
+ """
logging.debug('datastore.get')
if object_id.startswith('/'):
return RawObject(object_id)
- metadata = dbus_helpers.get_properties(object_id)
+ metadata = _get_data_store().get_properties(object_id, byte_arrays=True)
ds_object = DSObject(object_id, DSMetadata(metadata), None)
# TODO: register the object for updates
@@ -211,14 +248,59 @@ def get(object_id):
def create():
+ """Create a new DSObject.
+
+ Return: a DSObject
+
+ """
metadata = DSMetadata()
metadata['mtime'] = datetime.now().isoformat()
metadata['timestamp'] = int(time.time())
return DSObject(object_id=None, metadata=metadata, file_path=None)
+def _update_ds_entry(uid, properties, filename, transfer_ownership=False,
+ reply_handler=None, error_handler=None, timeout=-1):
+ debug_properties = properties.copy()
+ if "preview" in debug_properties:
+ debug_properties["preview"] = "<omitted>"
+ logging.debug('dbus_helpers.update: %s, %s, %s, %s', uid, filename,
+ debug_properties, transfer_ownership)
+ if reply_handler and error_handler:
+ _get_data_store().update(uid, dbus.Dictionary(properties), filename,
+ transfer_ownership,
+ reply_handler=reply_handler,
+ error_handler=error_handler,
+ timeout=timeout)
+ else:
+ _get_data_store().update(uid, dbus.Dictionary(properties),
+ filename, transfer_ownership)
+
+
+def _create_ds_entry(properties, filename, transfer_ownership=False):
+ object_id = _get_data_store().create(dbus.Dictionary(properties), filename,
+ transfer_ownership)
+ return object_id
+
+
def write(ds_object, update_mtime=True, transfer_ownership=False,
reply_handler=None, error_handler=None, timeout=-1):
+ """Write the DSObject given to the datastore. Creates a new entry if
+ the entry does not exist yet.
+
+ Keyword arguments:
+ update_mtime -- boolean if the mtime of the entry should be regenerated
+ (default True)
+ transfer_ownership -- set it to true if the ownership of the entry should
+ be passed - who is responsible to delete the file
+ when done with it (default False)
+ reply_handler -- will be called with the method's return values as
+ arguments (default None)
+ error_handler -- will be called with an instance of a DBusException
+ representing a remote exception (default None)
+ timeout -- dbus timeout for the caller to wait (default -1)
+
+ """
logging.debug('datastore.write')
properties = ds_object.metadata.get_dictionary().copy()
@@ -234,33 +316,71 @@ def write(ds_object, update_mtime=True, transfer_ownership=False,
# FIXME: this func will be sync for creates regardless of the handlers
# supplied. This is very bad API, need to decide what to do here.
if ds_object.object_id:
- dbus_helpers.update(ds_object.object_id,
- properties,
- file_path,
- transfer_ownership,
- reply_handler=reply_handler,
- error_handler=error_handler,
- timeout=timeout)
+ _update_ds_entry(ds_object.object_id,
+ properties,
+ file_path,
+ transfer_ownership,
+ reply_handler=reply_handler,
+ error_handler=error_handler,
+ timeout=timeout)
else:
if reply_handler or error_handler:
logging.warning('datastore.write() cannot currently be called' \
'async for creates, see ticket 3071')
- ds_object.object_id = dbus_helpers.create(properties,
- file_path,
- transfer_ownership)
+ ds_object.object_id = _create_ds_entry(properties, file_path,
+ transfer_ownership)
ds_object.metadata['uid'] = ds_object.object_id
# TODO: register the object for updates
logging.debug('Written object %s to the datastore.', ds_object.object_id)
def delete(object_id):
+ """Delete the datastore entry with the given uid.
+
+ Keyword arguments:
+ object_id -- uid of the datastore entry
+
+ """
logging.debug('datastore.delete')
- dbus_helpers.delete(object_id)
+ _get_data_store().delete(object_id)
def find(query, sorting=None, limit=None, offset=None, properties=None,
reply_handler=None, error_handler=None):
-
+ """Find DS entries that match the query provided.
+
+ Keyword arguments:
+ query -- a dictionary containing metadata key value pairs
+ for a fulltext search use the key 'query' e.g. {'query': 'blue*'}
+ other possible well-known properties are:
+ 'activity': 'my.organization.MyActivity'
+ 'activity_id': '6f7f3acacca87886332f50bdd522d805f0abbf1f'
+ 'title': 'My new project'
+ 'title_set_by_user': '0'
+ 'keep': '0'
+ 'ctime': '1972-05-12T18:41:08'
+ 'mtime': '2007-06-16T03:42:33'
+ 'timestamp': 1192715145
+ 'preview': ByteArray(png file data, 300x225 px)
+ 'icon-color': '#ff0000,#ffff00'
+ 'mime_type': 'application/x-my-activity'
+ 'share-scope': # if shared
+ 'buddies': '{}'
+ 'description': 'some longer text'
+ 'tags': 'one two'
+ sorting -- key to order results by e.g. 'timestamp' (default None)
+ limit -- return only limit results (default None)
+ offset -- return only results starting at offset (default None)
+ properties -- you can specify here a list of metadata you want to be
+ present in the result e.g. ['title, 'keep'] (default None)
+ reply_handler -- will be called with the method's return values as
+ arguments (default None)
+ error_handler -- will be called with an instance of a DBusException
+ representing a remote exception (default None)
+
+ Return: DSObjects matching the query, number of matches
+
+ """
query = query.copy()
if properties is None:
@@ -273,57 +393,103 @@ def find(query, sorting=None, limit=None, offset=None, properties=None,
if offset:
query['offset'] = offset
- props_list, total_count = dbus_helpers.find(query, properties,
- reply_handler, error_handler)
+ if reply_handler and error_handler:
+ _get_data_store().find(query, properties,
+ reply_handler=reply_handler,
+ error_handler=error_handler,
+ byte_arrays=True)
+ return
+ else:
+ entries, total_count = _get_data_store().find(query, properties,
+ byte_arrays=True)
+ ds_objects = []
+ for entry in entries:
+ object_id = entry['uid']
+ del entry['uid']
- objects = []
- for props in props_list:
- object_id = props['uid']
- del props['uid']
+ ds_object = DSObject(object_id, DSMetadata(entry), None)
+ ds_objects.append(ds_object)
- ds_object = DSObject(object_id, DSMetadata(props), None)
- objects.append(ds_object)
+ return ds_objects, total_count
- return objects, total_count
+def copy(ds_object, mount_point):
+ """Copy a datastore entry
-def copy(jobject, mount_point):
+ Keyword arguments:
+ ds_object -- DSObject to copy
+ mount_point -- mount point of the new datastore entry
- new_jobject = jobject.copy()
- new_jobject.metadata['mountpoint'] = mount_point
+ """
+ new_ds_object = ds_object.copy()
+ new_ds_object.metadata['mountpoint'] = mount_point
- if jobject.metadata.has_key('title'):
- filename = jobject.metadata['title']
+ if 'title' in ds_object.metadata:
+ filename = ds_object.metadata['title']
- if jobject.metadata.has_key('mime_type'):
- mime_type = jobject.metadata['mime_type']
+ if 'mime_type' in ds_object.metadata:
+ mime_type = ds_object.metadata['mime_type']
extension = mime.get_primary_extension(mime_type)
if extension:
filename += '.' + extension
- new_jobject.metadata['suggested_filename'] = filename
+ new_ds_object.metadata['suggested_filename'] = filename
# this will cause the file be retrieved from the DS
- new_jobject.file_path = jobject.file_path
+ new_ds_object.file_path = ds_object.file_path
- write(new_jobject)
+ write(new_ds_object)
def mount(uri, options, timeout=-1):
- return dbus_helpers.mount(uri, options, timeout=timeout)
+ """Deprecated. API private to the shell. Mount a device.
+
+ Keyword arguments:
+ uri -- identifier of the device
+ options -- mount options
+ timeout -- dbus timeout for the caller to wait (default -1)
+
+ Return: empty string
+
+ """
+ return _get_data_store().mount(uri, options, timeout=timeout)
def unmount(mount_point_id):
- dbus_helpers.unmount(mount_point_id)
+ """Deprecated. API private to the shell.
+
+ Keyword arguments:
+ mount_point_id -- id of the mount point
+
+ Note: API private to the shell.
+
+ """
+ _get_data_store().unmount(mount_point_id)
def mounts():
- return dbus_helpers.mounts()
+ """Deprecated. Returns the mount point of the datastore. We get mount
+ points through gio now. API private to the shell.
+
+ Return: datastore mount point
+
+ """
+ return _get_data_store().mounts()
def complete_indexing():
- return dbus_helpers.complete_indexing()
+ """Deprecated. API private to the shell."""
+ logging.warning('The method complete_indexing has been deprecated.')
def get_unique_values(key):
- return dbus_helpers.get_unique_values(key)
+ """Retrieve an array of unique values for a field.
+
+ Keyword arguments:
+ key -- only the property activity is currently supported
+
+ Return: list of activities
+
+ """
+ return _get_data_store().get_uniquevaluesfor(
+ key, dbus.Dictionary({}, signature='ss'))
diff --git a/src/sugar/datastore/dbus_helpers.py b/src/sugar/datastore/dbus_helpers.py
deleted file mode 100644
index 008b7a0..0000000
--- a/src/sugar/datastore/dbus_helpers.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# Copyright (C) 2006-2007 Red Hat, Inc.
-# Copyright (C) 2007, One Laptop Per Child
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-"""
-UNSTABLE. Should be internal to the datastore module.
-"""
-
-import logging
-
-import dbus
-import dbus.glib
-
-DS_DBUS_SERVICE = "org.laptop.sugar.DataStore"
-DS_DBUS_INTERFACE = "org.laptop.sugar.DataStore"
-DS_DBUS_PATH = "/org/laptop/sugar/DataStore"
-
-_data_store = None
-
-
-def _get_data_store():
- global _data_store
-
- if not _data_store:
- _bus = dbus.SessionBus()
- _data_store = dbus.Interface(_bus.get_object(DS_DBUS_SERVICE,
- DS_DBUS_PATH),
- DS_DBUS_INTERFACE)
- return _data_store
-
-
-def create(properties, filename, transfer_ownership=False):
- object_id = _get_data_store().create(dbus.Dictionary(properties), filename,
- transfer_ownership)
- logging.debug('dbus_helpers.create: ' + object_id)
- return object_id
-
-
-def update(uid, properties, filename, transfer_ownership=False,
- reply_handler=None, error_handler=None, timeout=-1):
- debug_props = properties.copy()
- if debug_props.has_key("preview"):
- debug_props["preview"] = "<omitted>"
- logging.debug('dbus_helpers.update: %s, %s, %s, %s', uid, filename,
- debug_props, transfer_ownership)
- if reply_handler and error_handler:
- _get_data_store().update(uid, dbus.Dictionary(properties), filename,
- transfer_ownership,
- reply_handler=reply_handler,
- error_handler=error_handler,
- timeout=timeout)
- else:
- _get_data_store().update(uid, dbus.Dictionary(properties),
- filename, transfer_ownership)
-
-
-def delete(uid):
- logging.debug('dbus_helpers.delete: %r', uid)
- _get_data_store().delete(uid)
-
-
-def get_properties(uid):
- logging.debug('dbus_helpers.get_properties: %s', uid)
- return _get_data_store().get_properties(uid, byte_arrays=True)
-
-
-def get_filename(uid):
- filename = _get_data_store().get_filename(uid)
- logging.debug('dbus_helpers.get_filename: %s, %s', uid, filename)
- return filename
-
-
-def find(query, properties, reply_handler, error_handler):
- logging.debug('dbus_helpers.find: %r %r', query, properties)
- if reply_handler and error_handler:
- return _get_data_store().find(query, properties,
- reply_handler=reply_handler, error_handler=error_handler,
- byte_arrays=True)
- else:
- return _get_data_store().find(query, properties, byte_arrays=True)
-
-
-def mount(uri, options, timeout=-1):
- return _get_data_store().mount(uri, options, timeout=timeout)
-
-
-def unmount(mount_point_id):
- _get_data_store().unmount(mount_point_id)
-
-
-def mounts():
- return _get_data_store().mounts()
-
-
-def get_unique_values(key):
- return _get_data_store().get_uniquevaluesfor(
- key, dbus.Dictionary({}, signature='ss'))
-
-
-def complete_indexing():
- return _get_data_store().complete_indexing()