~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_groupcompress_py.py

  • Committer: Vincent Ladeuil
  • Date: 2009-04-27 16:10:10 UTC
  • mto: (4310.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4311.
  • Revision ID: v.ladeuil+lp@free.fr-20090427161010-7swfzeagf63cpixd
Fix bug #367726 by reverting some default user handling introduced
while fixing bug #256612.

* bzrlib/transport/ssh.py:
(_paramiko_auth): Explicitly use getpass.getuser() as default
user.

* bzrlib/transport/ftp/_gssapi.py:
(GSSAPIFtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/transport/ftp/__init__.py:
(FtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/tests/test_sftp_transport.py:
(TestUsesAuthConfig.test_sftp_is_none_if_no_config)
(TestUsesAuthConfig.test_sftp_doesnt_prompt_username): Revert to
None as the default user.

* bzrlib/tests/test_remote.py:
(TestRemoteSSHTransportAuthentication): The really offending one:
revert to None as the default user.

* bzrlib/tests/test_config.py:
(TestAuthenticationConfig.test_username_default_no_prompt): Update
test (and some PEP8).

* bzrlib/smtp_connection.py:
(SMTPConnection._authenticate): Revert to None as the default
user.

* bzrlib/plugins/launchpad/account.py:
(_get_auth_user): Revert default value handling.

* bzrlib/config.py:
(AuthenticationConfig.get_user): Fix doc-string. Leave default
value handling to callers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
from bzrlib import osutils
24
24
 
25
25
 
 
26
class _OutputHandler(object):
 
27
    """A simple class which just tracks how to split up an insert request."""
 
28
 
 
29
    def __init__(self, out_lines, index_lines, min_len_to_index):
 
30
        self.out_lines = out_lines
 
31
        self.index_lines = index_lines
 
32
        self.min_len_to_index = min_len_to_index
 
33
        self.cur_insert_lines = []
 
34
        self.cur_insert_len = 0
 
35
 
 
36
    def add_copy(self, start_byte, end_byte):
 
37
        # The data stream allows >64kB in a copy, but to match the compiled
 
38
        # code, we will also limit it to a 64kB copy
 
39
        for start_byte in xrange(start_byte, end_byte, 64*1024):
 
40
            num_bytes = min(64*1024, end_byte - start_byte)
 
41
            copy_bytes = encode_copy_instruction(start_byte, num_bytes)
 
42
            self.out_lines.append(copy_bytes)
 
43
            self.index_lines.append(False)
 
44
 
 
45
    def _flush_insert(self):
 
46
        if not self.cur_insert_lines:
 
47
            return
 
48
        if self.cur_insert_len > 127:
 
49
            raise AssertionError('We cannot insert more than 127 bytes'
 
50
                                 ' at a time.')
 
51
        self.out_lines.append(chr(self.cur_insert_len))
 
52
        self.index_lines.append(False)
 
53
        self.out_lines.extend(self.cur_insert_lines)
 
54
        if self.cur_insert_len < self.min_len_to_index:
 
55
            self.index_lines.extend([False]*len(self.cur_insert_lines))
 
56
        else:
 
57
            self.index_lines.extend([True]*len(self.cur_insert_lines))
 
58
        self.cur_insert_lines = []
 
59
        self.cur_insert_len = 0
 
60
 
 
61
    def _insert_long_line(self, line):
 
62
        # Flush out anything pending
 
63
        self._flush_insert()
 
64
        line_len = len(line)
 
65
        for start_index in xrange(0, line_len, 127):
 
66
            next_len = min(127, line_len - start_index)
 
67
            self.out_lines.append(chr(next_len))
 
68
            self.index_lines.append(False)
 
69
            self.out_lines.append(line[start_index:start_index+next_len])
 
70
            # We don't index long lines, because we won't be able to match
 
71
            # a line split across multiple inserts anway
 
72
            self.index_lines.append(False)
 
73
 
 
74
    def add_insert(self, lines):
 
75
        if self.cur_insert_lines != []:
 
76
            raise AssertionError('self.cur_insert_lines must be empty when'
 
77
                                 ' adding a new insert')
 
78
        for line in lines:
 
79
            if len(line) > 127:
 
80
                self._insert_long_line(line)
 
81
            else:
 
82
                next_len = len(line) + self.cur_insert_len
 
83
                if next_len > 127:
 
84
                    # Adding this line would overflow, so flush, and start over
 
85
                    self._flush_insert()
 
86
                    self.cur_insert_lines = [line]
 
87
                    self.cur_insert_len = len(line)
 
88
                else:
 
89
                    self.cur_insert_lines.append(line)
 
90
                    self.cur_insert_len = next_len
 
91
        self._flush_insert()
 
92
 
 
93
 
26
94
class LinesDeltaIndex(object):
27
95
    """This class indexes matches between strings.
28
96
 
33
101
    :ivar endpoint: The total number of bytes in self.line_offsets
34
102
    """
35
103
 
 
104
    _MIN_MATCH_BYTES = 10
 
105
    _SOFT_MIN_MATCH_BYTES = 200
 
106
 
36
107
    def __init__(self, lines):
37
108
        self.lines = []
38
109
        self.line_offsets = []
50
121
        for idx, do_index in enumerate(index):
51
122
            if not do_index:
52
123
                continue
53
 
            matches.setdefault(new_lines[idx], []).append(start_idx + idx)
 
124
            line = new_lines[idx]
 
125
            try:
 
126
                matches[line].add(start_idx + idx)
 
127
            except KeyError:
 
128
                matches[line] = set([start_idx + idx])
54
129
 
55
130
    def get_matches(self, line):
56
131
        """Return the lines which match the line in right."""
59
134
        except KeyError:
60
135
            return None
61
136
 
62
 
    def _get_longest_match(self, lines, pos, locations):
 
137
    def _get_longest_match(self, lines, pos):
63
138
        """Look at all matches for the current line, return the longest.
64
139
 
65
140
        :param lines: The lines we are matching against
74
149
        """
75
150
        range_start = pos
76
151
        range_len = 0
77
 
        copy_ends = None
 
152
        prev_locations = None
78
153
        max_pos = len(lines)
 
154
        matching = self._matching_lines
79
155
        while pos < max_pos:
80
 
            if locations is None:
81
 
                # TODO: is try/except better than get(..., None)?
82
 
                try:
83
 
                    locations = self._matching_lines[lines[pos]]
84
 
                except KeyError:
85
 
                    locations = None
86
 
            if locations is None:
 
156
            try:
 
157
                locations = matching[lines[pos]]
 
158
            except KeyError:
87
159
                # No more matches, just return whatever we have, but we know
88
160
                # that this last position is not going to match anything
89
161
                pos += 1
90
162
                break
 
163
            # We have a match
 
164
            if prev_locations is None:
 
165
                # This is the first match in a range
 
166
                prev_locations = locations
 
167
                range_len = 1
 
168
                locations = None # Consumed
91
169
            else:
92
 
                # We have a match
93
 
                if copy_ends is None:
94
 
                    # This is the first match in a range
95
 
                    copy_ends = [loc + 1 for loc in locations]
96
 
                    range_len = 1
 
170
                # We have a match started, compare to see if any of the
 
171
                # current matches can be continued
 
172
                next_locations = locations.intersection([loc + 1 for loc
 
173
                                                         in prev_locations])
 
174
                if next_locations:
 
175
                    # At least one of the regions continues to match
 
176
                    prev_locations = set(next_locations)
 
177
                    range_len += 1
97
178
                    locations = None # Consumed
98
179
                else:
99
 
                    # We have a match started, compare to see if any of the
100
 
                    # current matches can be continued
101
 
                    next_locations = set(copy_ends).intersection(locations)
102
 
                    if next_locations:
103
 
                        # At least one of the regions continues to match
104
 
                        copy_ends = [loc + 1 for loc in next_locations]
105
 
                        range_len += 1
106
 
                        locations = None # Consumed
107
 
                    else:
108
 
                        # All current regions no longer match.
109
 
                        # This line does still match something, just not at the
110
 
                        # end of the previous matches. We will return locations
111
 
                        # so that we can avoid another _matching_lines lookup.
112
 
                        break
 
180
                    # All current regions no longer match.
 
181
                    # This line does still match something, just not at the
 
182
                    # end of the previous matches. We will return locations
 
183
                    # so that we can avoid another _matching_lines lookup.
 
184
                    break
113
185
            pos += 1
114
 
        if copy_ends is None:
 
186
        if prev_locations is None:
115
187
            # We have no matches, this is a pure insert
116
 
            return None, pos, locations
117
 
        return (((min(copy_ends) - range_len, range_start, range_len)),
118
 
                pos, locations)
 
188
            return None, pos
 
189
        smallest = min(prev_locations)
 
190
        return (smallest - range_len + 1, range_start, range_len), pos
119
191
 
120
192
    def get_matching_blocks(self, lines, soft=False):
121
193
        """Return the ranges in lines which match self.lines.
133
205
        # instructions.
134
206
        result = []
135
207
        pos = 0
136
 
        locations = None
137
208
        max_pos = len(lines)
138
209
        result_append = result.append
139
 
        min_match_bytes = 10
 
210
        min_match_bytes = self._MIN_MATCH_BYTES
140
211
        if soft:
141
 
            min_match_bytes = 200
 
212
            min_match_bytes = self._SOFT_MIN_MATCH_BYTES
142
213
        while pos < max_pos:
143
 
            block, pos, locations = self._get_longest_match(lines, pos,
144
 
                                                            locations)
 
214
            block, pos = self._get_longest_match(lines, pos)
145
215
            if block is not None:
146
216
                # Check to see if we match fewer than min_match_bytes. As we
147
217
                # will turn this into a pure 'insert', rather than a copy.
178
248
                ' got out of sync with the line counter.')
179
249
        self.endpoint = endpoint
180
250
 
181
 
    def _flush_insert(self, start_linenum, end_linenum,
182
 
                      new_lines, out_lines, index_lines):
183
 
        """Add an 'insert' request to the data stream."""
184
 
        bytes_to_insert = ''.join(new_lines[start_linenum:end_linenum])
185
 
        insert_length = len(bytes_to_insert)
186
 
        # Each insert instruction is at most 127 bytes long
187
 
        for start_byte in xrange(0, insert_length, 127):
188
 
            insert_count = min(insert_length - start_byte, 127)
189
 
            out_lines.append(chr(insert_count))
190
 
            # Don't index the 'insert' instruction
191
 
            index_lines.append(False)
192
 
            insert = bytes_to_insert[start_byte:start_byte+insert_count]
193
 
            as_lines = osutils.split_lines(insert)
194
 
            out_lines.extend(as_lines)
195
 
            index_lines.extend([True]*len(as_lines))
196
 
 
197
 
    def _flush_copy(self, old_start_linenum, num_lines,
198
 
                    out_lines, index_lines):
199
 
        if old_start_linenum == 0:
200
 
            first_byte = 0
201
 
        else:
202
 
            first_byte = self.line_offsets[old_start_linenum - 1]
203
 
        stop_byte = self.line_offsets[old_start_linenum + num_lines - 1]
204
 
        num_bytes = stop_byte - first_byte
205
 
        # The data stream allows >64kB in a copy, but to match the compiled
206
 
        # code, we will also limit it to a 64kB copy
207
 
        for start_byte in xrange(first_byte, stop_byte, 64*1024):
208
 
            num_bytes = min(64*1024, stop_byte - first_byte)
209
 
            copy_bytes = encode_copy_instruction(start_byte, num_bytes)
210
 
            out_lines.append(copy_bytes)
211
 
            index_lines.append(False)
212
 
 
213
251
    def make_delta(self, new_lines, bytes_length=None, soft=False):
214
252
        """Compute the delta for this content versus the original content."""
215
253
        if bytes_length is None:
217
255
        # reserved for content type, content length
218
256
        out_lines = ['', '', encode_base128_int(bytes_length)]
219
257
        index_lines = [False, False, False]
 
258
        output_handler = _OutputHandler(out_lines, index_lines,
 
259
                                        self._MIN_MATCH_BYTES)
220
260
        blocks = self.get_matching_blocks(new_lines, soft=soft)
221
261
        current_line_num = 0
222
262
        # We either copy a range (while there are reusable lines) or we
224
264
        for old_start, new_start, range_len in blocks:
225
265
            if new_start != current_line_num:
226
266
                # non-matching region, insert the content
227
 
                self._flush_insert(current_line_num, new_start,
228
 
                                   new_lines, out_lines, index_lines)
 
267
                output_handler.add_insert(new_lines[current_line_num:new_start])
229
268
            current_line_num = new_start + range_len
230
269
            if range_len:
231
 
                self._flush_copy(old_start, range_len, out_lines, index_lines)
 
270
                # Convert the line based offsets into byte based offsets
 
271
                if old_start == 0:
 
272
                    first_byte = 0
 
273
                else:
 
274
                    first_byte = self.line_offsets[old_start - 1]
 
275
                last_byte = self.line_offsets[old_start + range_len - 1]
 
276
                output_handler.add_copy(first_byte, last_byte)
232
277
        return out_lines, index_lines
233
278
 
234
279
 
271
316
            copy_bytes.append(chr(base_byte))
272
317
        offset >>= 8
273
318
    if length is None:
274
 
        # None is used by the test suite
275
 
        copy_bytes[0] = chr(copy_command)
276
 
        return ''.join(copy_bytes)
 
319
        raise ValueError("cannot supply a length of None")
277
320
    if length > 0x10000:
278
321
        raise ValueError("we don't emit copy records for lengths > 64KiB")
279
322
    if length == 0:
337
380
 
338
381
def make_delta(source_bytes, target_bytes):
339
382
    """Create a delta from source to target."""
340
 
    # TODO: The checks below may not be a the right place yet.
341
383
    if type(source_bytes) is not str:
342
384
        raise TypeError('source is not a str')
343
385
    if type(target_bytes) is not str: