/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/transport/remote.py

  • Committer: Jelmer Vernooij
  • Date: 2018-05-06 11:48:54 UTC
  • mto: This revision was merged to the branch mainline in revision 6960.
  • Revision ID: jelmer@jelmer.uk-20180506114854-h4qd9ojaqy8wxjsd
Move .mailmap to root.

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
 
25
25
__all__ = ['RemoteTransport', 'RemoteTCPTransport', 'RemoteSSHTransport']
26
26
 
27
 
from io import BytesIO
28
 
 
29
27
from .. import (
30
28
    config,
31
29
    debug,
37
35
from ..bzr import (
38
36
    remote,
39
37
    )
40
 
from ..sixish import PY3
 
38
from ..sixish import (
 
39
    BytesIO,
 
40
    )
41
41
from ..bzr.smart import client, medium
42
42
 
43
43
 
66
66
    """
67
67
 
68
68
    # When making a readv request, cap it at requesting 5MB of data
69
 
    _max_readv_bytes = 5 * 1024 * 1024
 
69
    _max_readv_bytes = 5*1024*1024
70
70
 
71
71
    # IMPORTANT FOR IMPLEMENTORS: RemoteTransport MUST NOT be given encoding
72
72
    # responsibilities: Put those on SmartClient or similar. This is vital for
102
102
        # what we want to share is really the shared connection.
103
103
 
104
104
        if (_from_transport is not None
105
 
                and isinstance(_from_transport, RemoteTransport)):
 
105
            and isinstance(_from_transport, RemoteTransport)):
106
106
            _client = _from_transport._client
107
107
        elif _from_transport is None:
108
108
            # If no _from_transport is specified, we need to intialize the
151
151
    def is_readonly(self):
152
152
        """Smart server transport can do read/write file operations."""
153
153
        try:
154
 
            resp = self._call2(b'Transport.is_readonly')
 
154
            resp = self._call2('Transport.is_readonly')
155
155
        except errors.UnknownSmartMethod:
156
156
            # XXX: nasty hack: servers before 0.16 don't have a
157
157
            # 'Transport.is_readonly' verb, so we do what clients before 0.16
158
158
            # did: assume False.
159
159
            return False
160
 
        if resp == (b'yes', ):
 
160
        if resp == ('yes', ):
161
161
            return True
162
 
        elif resp == (b'no', ):
 
162
        elif resp == ('no', ):
163
163
            return False
164
164
        else:
165
165
            raise errors.UnexpectedSmartServerResponse(resp)
172
172
 
173
173
    def _remote_path(self, relpath):
174
174
        """Returns the Unicode version of the absolute path for relpath."""
175
 
        path = urlutils.URL._combine_paths(self._parsed_url.path, relpath)
176
 
        if not isinstance(path, bytes):
177
 
            path = path.encode()
178
 
        return path
 
175
        return urlutils.URL._combine_paths(self._parsed_url.path, relpath)
179
176
 
180
177
    def _call(self, method, *args):
181
178
        resp = self._call2(method, *args)
188
185
        except errors.ErrorFromSmartServer as err:
189
186
            # The first argument, if present, is always a path.
190
187
            if args:
191
 
                context = {'relpath': args[0].decode('utf-8')}
 
188
                context = {'relpath': args[0]}
192
189
            else:
193
190
                context = {}
194
191
            self._translate_error(err, **context)
210
207
 
211
208
        :see: Transport.has()
212
209
        """
213
 
        resp = self._call2(b'has', self._remote_path(relpath))
214
 
        if resp == (b'yes', ):
 
210
        resp = self._call2('has', self._remote_path(relpath))
 
211
        if resp == ('yes', ):
215
212
            return True
216
 
        elif resp == (b'no', ):
 
213
        elif resp == ('no', ):
217
214
            return False
218
215
        else:
219
216
            raise errors.UnexpectedSmartServerResponse(resp)
228
225
    def get_bytes(self, relpath):
229
226
        remote = self._remote_path(relpath)
230
227
        try:
231
 
            resp, response_handler = self._client.call_expecting_body(
232
 
                b'get', remote)
 
228
            resp, response_handler = self._client.call_expecting_body('get', remote)
233
229
        except errors.ErrorFromSmartServer as err:
234
230
            self._translate_error(err, relpath)
235
 
        if resp != (b'ok', ):
 
231
        if resp != ('ok', ):
236
232
            response_handler.cancel_read_body()
237
233
            raise errors.UnexpectedSmartServerResponse(resp)
238
234
        return response_handler.read_body_bytes()
239
235
 
240
236
    def _serialise_optional_mode(self, mode):
241
237
        if mode is None:
242
 
            return b''
 
238
            return ''
243
239
        else:
244
 
            return ('%d' % mode).encode('ascii')
 
240
            return '%d' % mode
245
241
 
246
242
    def mkdir(self, relpath, mode=None):
247
 
        resp = self._call2(b'mkdir', self._remote_path(relpath),
248
 
                           self._serialise_optional_mode(mode))
 
243
        resp = self._call2('mkdir', self._remote_path(relpath),
 
244
            self._serialise_optional_mode(mode))
249
245
 
250
246
    def open_write_stream(self, relpath, mode=None):
251
247
        """See Transport.open_write_stream."""
252
 
        self.put_bytes(relpath, b"", mode)
 
248
        self.put_bytes(relpath, "", mode)
253
249
        result = transport.AppendBasedFileStream(self, relpath)
254
250
        transport._file_streams[self.abspath(relpath)] = result
255
251
        return result
256
252
 
257
253
    def put_bytes(self, relpath, raw_bytes, mode=None):
258
 
        if not isinstance(raw_bytes, bytes):
 
254
        if not isinstance(raw_bytes, str):
259
255
            raise TypeError(
260
 
                'raw_bytes must be bytes string, not %s' % type(raw_bytes))
 
256
                'raw_bytes must be a plain string, not %s' % type(raw_bytes))
261
257
        resp = self._call_with_body_bytes(
262
 
            b'put',
 
258
            'put',
263
259
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
264
260
            raw_bytes)
265
261
        self._ensure_ok(resp)
270
266
                             dir_mode=None):
271
267
        """See Transport.put_bytes_non_atomic."""
272
268
        # FIXME: no encoding in the transport!
273
 
        create_parent_str = b'F'
 
269
        create_parent_str = 'F'
274
270
        if create_parent_dir:
275
 
            create_parent_str = b'T'
 
271
            create_parent_str = 'T'
276
272
 
277
273
        resp = self._call_with_body_bytes(
278
 
            b'put_non_atomic',
 
274
            'put_non_atomic',
279
275
            (self._remote_path(relpath), self._serialise_optional_mode(mode),
280
276
             create_parent_str, self._serialise_optional_mode(dir_mode)),
281
277
            raw_bytes)
304
300
 
305
301
    def append_bytes(self, relpath, bytes, mode=None):
306
302
        resp = self._call_with_body_bytes(
307
 
            b'append',
 
303
            'append',
308
304
            (self._remote_path(relpath), self._serialise_optional_mode(mode)),
309
305
            bytes)
310
 
        if resp[0] == b'appended':
 
306
        if resp[0] == 'appended':
311
307
            return int(resp[1])
312
308
        raise errors.UnexpectedSmartServerResponse(resp)
313
309
 
314
310
    def delete(self, relpath):
315
 
        resp = self._call2(b'delete', self._remote_path(relpath))
 
311
        resp = self._call2('delete', self._remote_path(relpath))
316
312
        self._ensure_ok(resp)
317
313
 
318
314
    def external_url(self):
332
328
 
333
329
        sorted_offsets = sorted(offsets)
334
330
        coalesced = list(self._coalesce_offsets(sorted_offsets,
335
 
                                                limit=self._max_readv_combine,
336
 
                                                fudge_factor=self._bytes_to_read_before_seek,
337
 
                                                max_size=self._max_readv_bytes))
 
331
                               limit=self._max_readv_combine,
 
332
                               fudge_factor=self._bytes_to_read_before_seek,
 
333
                               max_size=self._max_readv_bytes))
338
334
 
339
335
        # now that we've coallesced things, avoid making enormous requests
340
336
        requests = []
364
360
        for cur_request in requests:
365
361
            try:
366
362
                result = self._client.call_with_body_readv_array(
367
 
                    (b'readv', self._remote_path(relpath),),
 
363
                    ('readv', self._remote_path(relpath),),
368
364
                    [(c.start, c.length) for c in cur_request])
369
365
                resp, response_handler = result
370
366
            except errors.ErrorFromSmartServer as err:
371
367
                self._translate_error(err, relpath)
372
368
 
373
 
            if resp[0] != b'readv':
 
369
            if resp[0] != 'readv':
374
370
                # This should raise an exception
375
371
                response_handler.cancel_read_body()
376
372
                raise errors.UnexpectedSmartServerResponse(resp)
390
386
        for c_offset in coalesced:
391
387
            if len(data) < c_offset.length:
392
388
                raise errors.ShortReadvError(relpath, c_offset.start,
393
 
                                             c_offset.length, actual=len(data))
 
389
                            c_offset.length, actual=len(data))
394
390
            for suboffset, subsize in c_offset.ranges:
395
 
                key = (c_offset.start + suboffset, subsize)
396
 
                this_data = data[data_offset + suboffset:
397
 
                                 data_offset + suboffset + subsize]
 
391
                key = (c_offset.start+suboffset, subsize)
 
392
                this_data = data[data_offset+suboffset:
 
393
                                 data_offset+suboffset+subsize]
398
394
                # Special case when the data is in-order, rather than packing
399
395
                # into a map and then back out again. Benchmarking shows that
400
396
                # this has 100% hit rate, but leave in the data_map work just
404
400
                #       not have a real string.
405
401
                if key == cur_offset_and_size:
406
402
                    yield cur_offset_and_size[0], this_data
407
 
                    try:
408
 
                        cur_offset_and_size = next_offset[0] = next(
409
 
                            offset_stack)
410
 
                    except StopIteration:
411
 
                        return
 
403
                    cur_offset_and_size = next_offset[0] = next(offset_stack)
412
404
                else:
413
405
                    data_map[key] = this_data
414
406
            data_offset += c_offset.length
417
409
            while cur_offset_and_size in data_map:
418
410
                this_data = data_map.pop(cur_offset_and_size)
419
411
                yield cur_offset_and_size[0], this_data
420
 
                try:
421
 
                    cur_offset_and_size = next_offset[0] = next(offset_stack)
422
 
                except StopIteration:
423
 
                    return
 
412
                cur_offset_and_size = next_offset[0] = next(offset_stack)
424
413
 
425
414
    def rename(self, rel_from, rel_to):
426
 
        self._call(b'rename',
 
415
        self._call('rename',
427
416
                   self._remote_path(rel_from),
428
417
                   self._remote_path(rel_to))
429
418
 
430
419
    def move(self, rel_from, rel_to):
431
 
        self._call(b'move',
 
420
        self._call('move',
432
421
                   self._remote_path(rel_from),
433
422
                   self._remote_path(rel_to))
434
423
 
435
424
    def rmdir(self, relpath):
436
 
        resp = self._call(b'rmdir', self._remote_path(relpath))
 
425
        resp = self._call('rmdir', self._remote_path(relpath))
437
426
 
438
427
    def _ensure_ok(self, resp):
439
 
        if resp[0] != b'ok':
 
428
        if resp[0] != 'ok':
440
429
            raise errors.UnexpectedSmartServerResponse(resp)
441
430
 
442
431
    def _translate_error(self, err, relpath=None):
448
437
            m.disconnect()
449
438
 
450
439
    def stat(self, relpath):
451
 
        resp = self._call2(b'stat', self._remote_path(relpath))
452
 
        if resp[0] == b'stat':
 
440
        resp = self._call2('stat', self._remote_path(relpath))
 
441
        if resp[0] == 'stat':
453
442
            return _SmartStat(int(resp[1]), int(resp[2], 8))
454
443
        raise errors.UnexpectedSmartServerResponse(resp)
455
444
 
456
 
    # def lock_read(self, relpath):
457
 
    # """Lock the given file for shared (read) access.
458
 
    # :return: A lock object, which should be passed to Transport.unlock()
459
 
    # """
460
 
    # The old RemoteBranch ignore lock for reading, so we will
461
 
    # continue that tradition and return a bogus lock object.
462
 
    # class BogusLock(object):
463
 
    # def __init__(self, path):
 
445
    ## def lock_read(self, relpath):
 
446
    ##     """Lock the given file for shared (read) access.
 
447
    ##     :return: A lock object, which should be passed to Transport.unlock()
 
448
    ##     """
 
449
    ##     # The old RemoteBranch ignore lock for reading, so we will
 
450
    ##     # continue that tradition and return a bogus lock object.
 
451
    ##     class BogusLock(object):
 
452
    ##         def __init__(self, path):
464
453
    ##             self.path = path
465
 
    # def unlock(self):
466
 
    # pass
467
 
    # return BogusLock(relpath)
 
454
    ##         def unlock(self):
 
455
    ##             pass
 
456
    ##     return BogusLock(relpath)
468
457
 
469
458
    def listable(self):
470
459
        return True
471
460
 
472
461
    def list_dir(self, relpath):
473
 
        resp = self._call2(b'list_dir', self._remote_path(relpath))
474
 
        if resp[0] == b'names':
475
 
            return [name.decode('utf-8') if PY3 else name for name in resp[1:]]
 
462
        resp = self._call2('list_dir', self._remote_path(relpath))
 
463
        if resp[0] == 'names':
 
464
            return [name.encode('ascii') for name in resp[1:]]
476
465
        raise errors.UnexpectedSmartServerResponse(resp)
477
466
 
478
467
    def iter_files_recursive(self):
479
 
        resp = self._call2(b'iter_files_recursive', self._remote_path(''))
480
 
        if resp[0] == b'names':
481
 
            return [name.decode('utf-8') if PY3 else name for name in resp[1:]]
 
468
        resp = self._call2('iter_files_recursive', self._remote_path(''))
 
469
        if resp[0] == 'names':
 
470
            return resp[1:]
482
471
        raise errors.UnexpectedSmartServerResponse(resp)
483
472
 
484
473
 
524
513
        if user is None:
525
514
            auth = config.AuthenticationConfig()
526
515
            user = auth.get_user('ssh', self._parsed_url.host,
527
 
                                 self._parsed_url.port)
 
516
                self._parsed_url.port)
528
517
        ssh_params = medium.SSHParams(self._parsed_url.host,
529
 
                                      self._parsed_url.port, user, self._parsed_url.password,
530
 
                                      bzr_remote_path)
 
518
                self._parsed_url.port, user, self._parsed_url.password,
 
519
                bzr_remote_path)
531
520
        client_medium = medium.SmartSSHClientMedium(self.base, ssh_params)
532
521
        return client_medium, (user, self._parsed_url.password)
533
522
 
593
582
        """See transport._redirected_to"""
594
583
        redirected = self._http_transport._redirected_to(source, target)
595
584
        if (redirected is not None
596
 
                and isinstance(redirected, type(self._http_transport))):
 
585
            and isinstance(redirected, type(self._http_transport))):
597
586
            return RemoteHTTPTransport('bzr+' + redirected.external_url(),
598
587
                                       http_transport=redirected)
599
588
        else:
602
591
 
603
592
 
604
593
class HintingSSHTransport(transport.Transport):
605
 
    """Simple transport that handles ssh:// and points out bzr+ssh:// and git+ssh://."""
606
 
 
607
 
    # TODO(jelmer): Implement support for detecting whether the repository at the
608
 
    # other end is a git or bzr repository.
 
594
    """Simple transport that handles ssh:// and points out bzr+ssh://."""
609
595
 
610
596
    def __init__(self, url):
611
 
        raise errors.UnsupportedProtocol(
612
 
            url, 'Use bzr+ssh for Bazaar operations over SSH, e.g. "bzr+%s". '
613
 
            'Use git+ssh for Git operations over SSH, e.g. "git+%s".' % (url, url))
 
597
        raise errors.UnsupportedProtocol(url,
 
598
            'bzr supports bzr+ssh to operate over ssh, use "bzr+%s".' % url)
614
599
 
615
600
 
616
601
def get_test_permutations():
617
602
    """Return (transport, server) permutations for testing."""
618
 
    # We may need a little more test framework support to construct an
619
 
    # appropriate RemoteTransport in the future.
 
603
    ### We may need a little more test framework support to construct an
 
604
    ### appropriate RemoteTransport in the future.
620
605
    from ..tests import test_server
621
606
    return [(RemoteTCPTransport, test_server.SmartTCPServer_for_testing)]