33
_decode_utf8 = cache_utf8.decode
36
def _validate_properties(props):
38
# TODO: we really want an 'isascii' check for key
39
unicode_props = dict((key, _decode_utf8(value))
40
for key, value in props.iteritems())
44
def _is_format_10(value):
46
raise ValueError('Format number was not recognized, expected 10 got %d'
33
51
class BEncodeRevisionSerializer1(object):
34
"""Simple revision serializer based around bencode.
52
"""Simple revision serializer based around bencode.
55
# Maps {key:(Revision attribute, bencode_type, validator)}
56
# This tells us what kind we expect bdecode to create, what variable on
57
# Revision we should be using, and a function to call to validate/transform
59
# TODO: add a 'validate_utf8' for things like revision_id and file_id
60
# and a validator for parent-ids
61
_schema = {'format': (None, int, _is_format_10),
62
'committer': ('committer', str, _decode_utf8),
63
'timezone': ('timezone', int, None),
64
'timestamp': ('timestamp', str, float),
65
'revision-id': ('revision_id', str, None),
66
'parent-ids': ('parent_ids', list, tuple),
67
'inventory-sha1': ('inventory_sha1', str, None),
68
'message': ('message', str, _decode_utf8),
69
'properties': ('properties', dict, _validate_properties),
38
72
def write_revision_to_string(self, rev):
39
73
encode_utf8 = cache_utf8.encode
41
"revision-id": rev.revision_id,
42
"timestamp": "%.3f" % rev.timestamp,
43
"parent-ids": rev.parent_ids,
44
"inventory-sha1": rev.inventory_sha1,
45
"committer": encode_utf8(rev.committer),
46
"message": encode_utf8(rev.message),
74
# Use a list of tuples rather than a dict
75
# This lets us control the ordering, so that we are able to create
79
("committer", encode_utf8(rev.committer)),
81
if rev.timezone is not None:
82
ret.append(("timezone", rev.timezone))
83
# For bzr revisions, the most common property is just 'branch-nick'
84
# which changes infrequently.
49
86
for key, value in rev.properties.iteritems():
50
87
revprops[key] = encode_utf8(value)
51
ret["properties"] = revprops
52
if rev.timezone is not None:
53
ret["timezone"] = rev.timezone
88
ret.append(('properties', revprops))
90
("timestamp", "%.3f" % rev.timestamp),
91
("revision-id", rev.revision_id),
92
("parent-ids", rev.parent_ids),
93
("inventory-sha1", rev.inventory_sha1),
94
("message", encode_utf8(rev.message)),
54
96
return bencode.bencode(ret)
56
98
def write_revision(self, rev, f):
57
99
f.write(self.write_revision_to_string(rev))
59
101
def read_revision_from_string(self, text):
102
# TODO: consider writing a Revision decoder, rather than using the
103
# generic bencode decoder
60
104
decode_utf8 = cache_utf8.decode
61
105
ret = bencode.bdecode(text)
62
rev = _mod_revision.Revision(
63
committer=decode_utf8(ret["committer"]),
64
revision_id=ret["revision-id"],
65
parent_ids=ret["parent-ids"],
66
inventory_sha1=ret["inventory-sha1"],
67
timestamp=float(ret["timestamp"]),
68
message=decode_utf8(ret["message"]),
71
rev.timezone = ret["timezone"]
106
if not isinstance(ret, list):
107
raise ValueError("invalid revision text")
108
schema = dict(self._schema)
110
for key, value in ret:
111
# The entry must be present in allowed keys, but only present a
113
var_name, expected_type, validator = schema.pop(key)
114
if value.__class__ is not expected_type:
115
raise ValueError('key %s did not conform to the expected type'
117
% (key, expected_type, type(value)))
118
if validator is not None:
119
value = validator(value)
120
if var_name is not None:
121
bits[var_name] = value
122
if schema.keys() not in ([], ['timezone']):
123
raise ValueError('Revision text was missing expected keys %s,'
124
' text %r' % (schema.keys(), text))
125
rev = _mod_revision.Revision(**bits)
126
if "timezone" not in bits:
73
127
rev.timezone = None
74
for key, value in ret["properties"].iteritems():
75
rev.properties[key] = decode_utf8(value)
78
130
def read_revision(self, f):