~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/chk_serializer.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-03 14:47:56 UTC
  • mto: This revision was merged to the branch mainline in revision 4410.
  • Revision ID: john@arbash-meinel.com-20090603144756-biptv8wwy4rybhze
Update the CHK Serializer to do lots more validation.
We now validate the types of the objects being read, and we mutate
the values appropriately.
We assert that we have enough values in the stream, and that we don't
have any duplicates, etc.

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
    xml6,
31
31
    )
32
32
 
 
33
_decode_utf8 = cache_utf8.decode
 
34
 
 
35
 
 
36
def _validate_properties(props):
 
37
    decode = _decode_utf8
 
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())
 
41
    return unicode_props
 
42
 
 
43
 
 
44
def _is_format_10(value):
 
45
    if value != 10:
 
46
        raise ValueError('Format number was not recognized, expected 10 got %d'
 
47
                         % (value,))
 
48
    return 10
 
49
 
 
50
 
33
51
class BEncodeRevisionSerializer1(object):
34
 
    """Simple revision serializer based around bencode. 
35
 
    
 
52
    """Simple revision serializer based around bencode.
36
53
    """
37
54
 
 
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
 
58
    # the type.
 
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),
 
70
    }
 
71
 
38
72
    def write_revision_to_string(self, rev):
39
73
        encode_utf8 = cache_utf8.encode
40
 
        ret = {
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),
47
 
            }
 
74
        # Use a list of tuples rather than a dict
 
75
        # This lets us control the ordering, so that we are able to create
 
76
        # smaller deltas
 
77
        ret = [
 
78
            ("format", 10),
 
79
            ("committer", encode_utf8(rev.committer)),
 
80
        ]
 
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.
48
85
        revprops = {}
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))
 
89
        ret.extend([
 
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)),
 
95
        ])
54
96
        return bencode.bencode(ret)
55
97
 
56
98
    def write_revision(self, rev, f):
57
99
        f.write(self.write_revision_to_string(rev))
58
100
 
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"]),
69
 
            properties={})
70
 
        if "timezone" in ret:
71
 
            rev.timezone = ret["timezone"]
72
 
        else:
 
106
        if not isinstance(ret, list):
 
107
            raise ValueError("invalid revision text")
 
108
        schema = dict(self._schema)
 
109
        bits = {}
 
110
        for key, value in ret:
 
111
            # The entry must be present in allowed keys, but only present a
 
112
            # single time
 
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'
 
116
                                 ' %s, but was %s'
 
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)
76
128
        return rev
77
129
 
78
130
    def read_revision(self, f):