diff options
author | Sascha Silbe <sascha-pgp@silbe.org> | 2012-01-05 22:15:57 (GMT) |
---|---|---|
committer | Sascha Silbe <sascha-pgp@silbe.org> | 2012-01-05 22:15:57 (GMT) |
commit | 4e66b46002673e648dce40ea294c1fe410d6ed94 (patch) | |
tree | 7de8153a59cbbd8328a630c75d7147ff65f2c839 | |
parent | 3bfb2d72e10b6c5fd8bcabc951f409775c4353f9 (diff) |
fsemulation: add support for by-tags
Add (resp. bring back, in the case of datastore-fuse) support for listing
data store entries by tag (still just a single tag, not recursive).
-rw-r--r-- | fsemulation.py | 146 |
1 files changed, 138 insertions, 8 deletions
diff --git a/fsemulation.py b/fsemulation.py index 2c38449..5f35bf7 100644 --- a/fsemulation.py +++ b/fsemulation.py @@ -34,7 +34,7 @@ DS_DBUS_PATH2 = '/org/laptop/sugar/DataStore2' DBUS_TIMEOUT_MAX = 2 ** 31 / 1000 DBUS_PYTHON_VALUE_ERROR = 'org.freedesktop.DBus.Python.ValueError' -_FILE_NAME_PROPS = ['mime_type', 'timestamp', 'title'] +_USEFUL_PROPS = ['mime_type', 'tags', 'timestamp', 'title'] """Metadata properties used for determining the file name of an entry""" @@ -149,7 +149,7 @@ class DataStore(object): """ query = query or {} - properties = list(_FILE_NAME_PROPS) + properties = list(_USEFUL_PROPS) if self._data_store.dbus_interface == DS_DBUS_INTERFACE2: properties += ['parent_id', 'tree_id', 'version_id'] options = {'metadata': properties, @@ -184,6 +184,26 @@ class DataStore(object): """Retrieve the tree_ids of all (matching) data store entries""" return [unicode(entry[0]) for entry in self.list_object_ids(query)] + def list_property_values(self, name, query=None): + """Return all unique values of the given property""" + assert isinstance(name, unicode) + + query = query or {} + if self._data_store.dbus_interface == DS_DBUS_INTERFACE2: + options = {'metadata': [name], 'all_versions': True} + entries = self._data_store.find(query, options, + timeout=DBUS_TIMEOUT_MAX, + byte_arrays=True)[0] + else: + # We can't use get_uniquevaluesfor() as sugar-datastore + # only supports it for activity_id, which is not what + # we need. + entries = self._data_store.find(query, [name], + timeout=DBUS_TIMEOUT_MAX, + byte_arrays=True)[0] + + return dict.fromkeys([entry.get(name) for entry in entries]).keys() + def check_object_id(self, object_id): """Return True if the given object_id identifies a data store entry""" try: @@ -203,6 +223,27 @@ class DataStore(object): byte_arrays=True)[0] return bool(results) + def check_property_contains(self, name, word): + """Return True if there is at least one entry containing word in the + given property + """ + assert isinstance(name, unicode) + assert isinstance(word, unicode) + + query_string = u'%s:"%s"' % (name, word.replace(u'"', u'')) + if self._data_store.dbus_interface == DS_DBUS_INTERFACE2: + options = {'limit': 1} + results = self._data_store.text_search({}, query_string, options, + timeout=DBUS_TIMEOUT_MAX, + byte_arrays=True)[0] + else: + query = {'query': query_string, 'limit': 1} + results = self._data_store.find(query, [name], + timeout=DBUS_TIMEOUT_MAX, + byte_arrays=True)[0] + + return bool(results) + def get_properties(self, object_id, names=None): """Read given properties for data store entry identified by object_id @@ -699,10 +740,99 @@ class ByTreeIdDirectory(Directory): self, tree_id)) +class ByTagsSubDirectory(ByTitleDirectory): + def __init__(self, file_system, level, parent, tags): + self._tags = frozenset(tags) + ByTitleDirectory.__init__(self, file_system, level, parent) + + def mknod(self, name): + if self._fs.try_resolve_title_name(name): + raise IOError(errno.EEXIST, os.strerror(errno.EEXIST)) + + props = {'title': name, 'tags': ' '.join(self._tags)} + object_id_ = self._ds.create_new(props) + + def listdir(self): + for name in Directory.listdir(self): + yield name + + for object_id, metadata in self._find_entries(): + name = self._fs.lookup_title_name(object_id, metadata) + yield name + + def readdir(self): + for name, entry in Directory.readdir(self): + yield name, entry + + for object_id, metadata in self._find_entries(): + name = self._fs.lookup_title_name(object_id, metadata) + yield (name, self._get_symlink(object_id)) + + def _find_entries(self): + query = {'query': ' '.join(self._tags)} + for object_id, props in self._ds.list_metadata(query): + entry_tags = frozenset(props.get('tags', '').split()) + if self._tags - entry_tags: + continue + + yield object_id, props + + +class ByTagsDirectory(Directory): + def __init__(self, file_system, level, parent): + Directory.__init__(self, file_system, level, 0550, parent) + self._tag_dirs = {} + + def listdir(self): + for name in Directory.listdir(self): + yield name + + for tag in self._list_tags(): + if u'/' in tag or tag.startswith(u'.'): + continue + + yield tag + + def readdir(self): + for name, entry in Directory.readdir(self): + yield name, entry + + for tag in self._list_tags(): + if u'/' in tag or tag.startswith(u'.'): + continue + + if tag not in self._tag_dirs: + self._tag_dirs[tag] = ByTagsSubDirectory(self._fs, + self._level + 1, + self, [tag]) + yield (tag, self._tag_dirs[tag]) + + def lookup(self, name): + if name not in self._tag_dirs: + if not self._check_tag(name): + raise IOError(errno.ENOENT, os.strerror(errno.ENOENT)) + + self._tag_dirs[name] = ByTagsSubDirectory(self._fs, + self._level + 1, self, + [name]) + return self._tag_dirs[name] + + def _check_tag(self, name): + return self._ds.check_property_contains(u'tags', name) + + def _list_tags(self): + tags = set() + for value in self._ds.list_property_values(u'tags'): + tags.update((value or u'').split()) + + tags.discard(u'') + return tags + + class RootDirectory(Directory): def __init__(self, file_system, mode): Directory.__init__(self, file_system, 0, mode, None) - #self._by_tags_directory = ByTagsDirectory(u'/by-tags', u'/', filesystem) + self._by_tags_directory = ByTagsDirectory(file_system, 1, self) self._by_title_directory = ByTitleDirectory(file_system, 1, self) if self._ds.supports_versions: self._by_id_directory = ByTreeIdDirectory(file_system, 1, self) @@ -714,7 +844,7 @@ class RootDirectory(Directory): yield name yield u'by-id' - #yield u'by-tags' + yield u'by-tags' yield u'by-title' def readdir(self): @@ -722,14 +852,14 @@ class RootDirectory(Directory): yield name, entry yield (u'by-id', self._by_id_directory) - #yield (u'by-tags', self._by_tags_directory) + yield (u'by-tags', self._by_tags_directory) yield (u'by-title', self._by_title_directory) def lookup(self, name): if name == u'by-id': return self._by_id_directory -# elif name == u'by-tags': -# return self._by_tags_directory + elif name == u'by-tags': + return self._by_tags_directory elif name == u'by-title': return self._by_title_directory @@ -799,7 +929,7 @@ class FSEmulation(object): if metadata is None: metadata = self.data_store.get_properties(object_id, - _FILE_NAME_PROPS) + _USEFUL_PROPS) name = self._generate_title_name(metadata, object_id) self._add_title_name(name, object_id) |