diff options
author | Sascha Silbe <sascha-pgp@silbe.org> | 2012-01-05 22:30:41 (GMT) |
---|---|---|
committer | Sascha Silbe <sascha-pgp@silbe.org> | 2012-01-05 22:30:41 (GMT) |
commit | 0881095aae503630401bc633b17821e9bc4fbbd5 (patch) | |
tree | c47c98834400676d0bfec69d1d8f27338892c07b | |
parent | 36dba509b1e485c8b3fa4835e5d7c442cf56fa85 (diff) | |
parent | 4e66b46002673e648dce40ea294c1fe410d6ed94 (diff) |
Merge remote-tracking branch 'datastore-fuse/master'
* datastore-fuse/master: (2 commits)
fsemulation: add support for by-tags
fsemulation: Fix '.' and '..' directory entries
-rw-r--r-- | fsemulation.py | 186 |
1 files changed, 168 insertions, 18 deletions
diff --git a/fsemulation.py b/fsemulation.py index e1dff69..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 @@ -594,13 +635,17 @@ class ByTitleDirectory(Directory): Directory.__init__(self, file_system, level, 0550, parent) def listdir(self): - Directory.listdir(self) + for name in Directory.listdir(self): + yield name + for object_id in self._ds.list_object_ids(): name = self._fs.lookup_title_name(object_id) yield name def readdir(self): - Directory.readdir(self) + for name, entry in Directory.readdir(self): + yield name, entry + for object_id, metadata in self._ds.list_metadata(): name = self._fs.lookup_title_name(object_id, metadata) yield (name, self._get_symlink(object_id)) @@ -627,12 +672,16 @@ class ByUidDirectory(Directory): return DSObject(self._fs, object_id) def listdir(self): - Directory.listdir(self) + for name in Directory.listdir(self): + yield name + for object_id in self._ds.list_object_ids(): yield object_id def readdir(self): - Directory.readdir(self) + for name, entry in Directory.readdir(self): + yield name, entry + for object_id in self._ds.list_object_ids(): yield (object_id, DSObject(self._fs, object_id)) @@ -650,12 +699,16 @@ class ByVersionIdDirectory(Directory): return DSObject(self._fs, object_id) def listdir(self): - Directory.listdir(self) + for name in Directory.listdir(self): + yield name + for version_id in self._ds.list_versions(self._tree_id): yield (self._tree_id, version_id) def readdir(self): - Directory.readdir(self) + for name, entry in Directory.readdir(self): + yield name, entry + for version_id in self._ds.list_versions(self._tree_id): object_id = (self._tree_id, version_id) yield (object_id, DSObject(self._fs, object_id)) @@ -672,21 +725,114 @@ class ByTreeIdDirectory(Directory): return ByVersionIdDirectory(self._fs, self._level + 1, self, tree_id) def listdir(self): - Directory.listdir(self) + for name in Directory.listdir(self): + yield name + for tree_id in self._ds.list_tree_ids(): yield tree_id def readdir(self): - Directory.readdir(self) + for name, entry in Directory.readdir(self): + yield name, entry + for tree_id in self._ds.list_tree_ids(): yield (tree_id, ByVersionIdDirectory(self._fs, self._level + 1, 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) @@ -694,22 +840,26 @@ class RootDirectory(Directory): self._by_id_directory = ByUidDirectory(file_system, 1, self) def listdir(self): - Directory.listdir(self) + for name in Directory.listdir(self): + yield name + yield u'by-id' - #yield u'by-tags' + yield u'by-tags' yield u'by-title' def readdir(self): - Directory.readdir(self) + for name, entry in Directory.readdir(self): + 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 @@ -779,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) |