1
# ubuntuone.storageprotocol.dircontent - directory content handling
3
# Author: Tim Cole <tim.cole@canonical.com>
5
# Copyright 2009 Canonical Ltd.
7
# This program is free software: you can redistribute it and/or modify it
8
# under the terms of the GNU Affero General Public License version 3,
9
# as published by the Free Software Foundation.
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranties of
13
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
# PURPOSE. See the GNU Affero General Public License for more details.
16
# You should have received a copy of the GNU Affero General Public License
17
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
Standard routines for working with directory content.
23
# DIRECTORY, FILE, and SYMLINK are for re-export
24
# pylint: disable-msg=W0611
25
from ubuntuone.storageprotocol.dircontent_pb2 import \
26
DirectoryContent, DIRECTORY, FILE, SYMLINK
28
def parse_dir_content(stream):
29
"""Unserializes directory content from a stream.
31
@param stream: an IO-alike stream object
32
@return: a generator yielding DirEntry objects
35
raw_content = stream.read()
36
unserialized_content = DirectoryContent()
37
# XXX: what exceptions can protobuf's parser raise?
38
unserialized_content.ParseFromString(raw_content)
40
for entry in unserialized_content.entries:
41
yield DirEntry(name=entry.name, node_type=entry.node_type,
44
def by_utf8_name(entry_a, entry_b):
45
"""Compares two entries by the UTF-8 form of their names."""
46
return cmp(entry_a.utf8_name, entry_b.utf8_name)
48
def write_dir_content(entries, stream):
49
"""Takes a sequence of DirEntry objects, sorts them, and writes
50
the corresponding serialized directory content to the given stream.
52
@param entries: an iterator producing DirEntry objects
53
@param stream: an IO-compatible stream to write to
56
sorted_entries = sorted(entries, by_utf8_name)
57
for chunk in yield_presorted_dir_content(sorted_entries):
60
def yield_presorted_dir_content(sorted_entries):
61
"""Takes a presorted sequence of DirEntry objects and yields each
62
chunks of serialized content.
64
@param sorted_entries: a presorted sequence of DirEntry objects
68
# A series of concatenated DirectoryContent objects is equivalent to
69
# a single DirectoryContent object with fields repeated. Among other
70
# things, this makes it easy to re-use the same protobuf objects for
71
# each entry that we serialize.
72
content = DirectoryContent()
73
pb_entry = content.entries.add()
75
for entry in sorted_entries:
76
pb_entry.name = entry.name
77
pb_entry.node_type = entry.node_type
78
pb_entry.node = entry.uuid
79
yield content.SerializeToString()
82
class DirEntry(object):
83
"""An object representing a directory entry.
85
@attr name: the node's name in the directory as a unicode string
86
@attr utf8_name: the node's name encoded in UTF-8
87
@attr node_type: the node's type (one of FILE, DIRECTORY, or SYMLINK)
88
@attr uuid: the node's server-side UUID
92
def __init__(self, name=None, utf8_name=None, node_type=None, uuid=None):
93
"""Initializes a directory entry object. Providing either the unicode
94
or UTF-8 names will result in both name fields being set.
96
@param name: the node's name in the directory
97
@param utf8_name: the node's name encoded in UTF-8
98
@param node_type: the node's type
99
@param uuid: the node's server-sude UUID
104
elif utf8_name is not None:
105
self.name = utf8_name.decode("utf-8")
109
if utf8_name is not None:
110
self.utf8_name = utf8_name
111
elif name is not None:
112
self.utf8_name = name.encode("utf-8")
114
self.utf8_name = None
116
self.node_type = node_type
119
def __eq__(self, other):
120
if isinstance(other, DirEntry):
121
return self.name == other.name and \
122
self.utf8_name == other.utf8_name and \
123
self.node_type == other.node_type and \
124
self.uuid == other.uuid