/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Container format for Bazaar data.
18
19
"Containers" and "records" are described in doc/developers/container-format.txt.
20
"""
21
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
22
import re
23
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
24
from bzrlib import errors
25
26
2506.5.3 by Andrew Bennetts
Change format marker to use the word 'Bazaar' rather than 'bzr'.
27
FORMAT_ONE = "Bazaar pack format 1"
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
28
29
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
30
_whitespace_re = re.compile('[\t\n\x0b\x0c\r ]')
31
32
33
def _check_name(name):
34
    """Do some basic checking of 'name'.
35
    
36
    At the moment, this just checks that there are no whitespace characters in a
37
    name.
38
39
    :raises InvalidRecordError: if name is not valid.
40
    """
41
    # XXX: consider checking that name is a str of valid UTF-8 too?
42
    if _whitespace_re.search(name) is not None:
43
        raise errors.InvalidRecordError("%r is not a valid name." % (name,))
44
45
2506.3.1 by Andrew Bennetts
More progress:
46
class ContainerWriter(object):
47
    """A class for writing containers."""
48
49
    def __init__(self, write_func):
50
        """Constructor.
51
52
        :param write_func: a callable that will be called when this
53
            ContainerWriter needs to write some bytes.
54
        """
55
        self.write_func = write_func
56
57
    def begin(self):
58
        """Begin writing a container."""
59
        self.write_func(FORMAT_ONE + "\n")
60
61
    def end(self):
62
        """Finish writing a container."""
63
        self.write_func("E")
64
65
    def add_bytes_record(self, bytes, names):
66
        """Add a Bytes record with the given names."""
67
        # Kind marker
68
        self.write_func("B")
69
        # Length
70
        self.write_func(str(len(bytes)) + "\n")
71
        # Names
72
        for name in names:
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
73
            # Make sure we're writing valid names.  Note that we will leave a
74
            # half-written record if a name is bad!
75
            _check_name(name)
2506.3.1 by Andrew Bennetts
More progress:
76
            self.write_func(name + "\n")
77
        # End of headers
78
        self.write_func("\n")
79
        # Finally, the contents.
80
        self.write_func(bytes)
81
82
83
class BaseReader(object):
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
84
85
    def __init__(self, reader_func):
86
        """Constructor.
87
88
        :param reader_func: a callable that takes one optional argument,
89
            ``size``, and returns at most that many bytes.  When the callable
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
90
            returns less than the requested number of bytes, then the end of the
91
            file/stream has been reached.
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
92
        """
93
        self.reader_func = reader_func
94
2506.3.1 by Andrew Bennetts
More progress:
95
    def _read_line(self):
96
        """Read a line from the input stream.
97
98
        This is a simple but inefficient implementation that just reads one byte
99
        at a time.  Lines should not be very long, so this is probably
100
        tolerable.
101
102
        :returns: a line, without the trailing newline
103
        """
104
        # XXX: Have a maximum line length, to prevent malicious input from
105
        # consuming an unreasonable amount of resources?
106
        #   -- Andrew Bennetts, 2007-05-07.
107
        line = ''
108
        while not line.endswith('\n'):
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
109
            byte = self.reader_func(1)
110
            if byte == '':
111
                raise errors.UnexpectedEndOfContainerError()
112
            line += byte
2506.3.1 by Andrew Bennetts
More progress:
113
        return line[:-1]
114
115
116
class ContainerReader(BaseReader):
117
    """A class for reading Bazaar's container format."""
118
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
119
    def iter_records(self):
120
        """Iterate over the container, yielding each record as it is read.
121
122
        Each yielded record will be a 2-tuple of (names, bytes), where names is
2506.4.1 by Andrew Bennetts
Fix docstring markup, remove obsolete comment.
123
        a ``list`` and bytes is a ``str``.
2506.3.1 by Andrew Bennetts
More progress:
124
125
        :raises UnknownContainerFormatError: if the format of the container is
126
            unrecognised.
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
127
        """
128
        format = self._read_line()
129
        if format != FORMAT_ONE:
130
            raise errors.UnknownContainerFormatError(format)
131
        return self._iter_records()
132
    
133
    def _iter_records(self):
134
        while True:
135
            record_kind = self.reader_func(1)
136
            if record_kind == 'B':
137
                # Bytes record.
2506.3.1 by Andrew Bennetts
More progress:
138
                reader = BytesRecordReader(self.reader_func)
139
                yield reader.read()
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
140
            elif record_kind == 'E':
141
                # End marker.  There are no more records.
142
                return
143
            elif record_kind == '':
144
                # End of stream encountered, but no End Marker record seen, so
145
                # this container is incomplete.
146
                raise errors.UnexpectedEndOfContainerError()
147
            else:
148
                # Unknown record type.
149
                raise errors.UnknownRecordTypeError(record_kind)
150
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
151
    def validate(self):
152
        """Validate this container and its records.
153
154
        You can either validate or iter_records, you can't do both.
155
156
        :raises ContainerError: if something is invalid.
157
        """
158
        for names, bytes in self.iter_records():
159
            # XXX: bytes is str, should be callable to get bytes.
160
            # callable()
161
            pass
162
        excess_bytes = self.reader_func(1)
163
        if excess_bytes != '':
164
            raise errors.ContainerHasExcessDataError(excess_bytes)
165
2506.3.1 by Andrew Bennetts
More progress:
166
167
class BytesRecordReader(BaseReader):
168
169
    def read(self):
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
170
        """Read this record.
171
172
        You can either validate or read, you can't do both.
173
174
        :returns: (names, bytes)
175
        """
2506.3.1 by Andrew Bennetts
More progress:
176
        # Read the content length.
177
        length_line = self._read_line()
178
        try:
179
            length = int(length_line)
180
        except ValueError:
181
            raise errors.InvalidRecordError(
182
                "%r is not a valid length." % (length_line,))
183
        
184
        # Read the list of names.
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
185
        names = []
186
        while True:
187
            name = self._read_line()
188
            if name == '':
189
                break
2506.5.2 by Andrew Bennetts
Raise InvalidRecordError on invalid names.
190
            _check_name(name)
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
191
            names.append(name)
192
        bytes = self.reader_func(length)
2506.3.3 by Andrew Bennetts
Deal with EOF in the middle of a bytes record.
193
        if len(bytes) != length:
194
            raise errors.UnexpectedEndOfContainerError()
2506.2.1 by Andrew Bennetts
Start implementing container format reading and writing.
195
        return names, bytes
196
2506.2.6 by Andrew Bennetts
Add validate method to ContainerReader and BytesRecordReader.
197
    def validate(self):
198
        """Validate this record.
199
200
        You can either validate or read, you can't do both.
201
202
        :raises ContainerError: if this record is invalid.
203
        """
204
        self.read()