~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/__init__.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:13:13 UTC
  • mfrom: (6614.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201191313-wdfvmfff1djde6oq
(vila) Release 2.7.0 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
26
26
it.
27
27
"""
28
28
 
 
29
from __future__ import absolute_import
 
30
 
29
31
from cStringIO import StringIO
30
32
import sys
31
33
 
33
35
lazy_import(globals(), """
34
36
import errno
35
37
from stat import S_ISDIR
36
 
import urllib
37
38
import urlparse
38
39
 
39
40
from bzrlib import (
51
52
from bzrlib.trace import (
52
53
    mutter,
53
54
    )
54
 
from bzrlib import registry
 
55
from bzrlib import (
 
56
    hooks,
 
57
    registry,
 
58
    )
55
59
 
56
60
 
57
61
# a dictionary of open file streams. Keys are absolute paths, values are
134
138
def register_lazy_transport(prefix, module, classname):
135
139
    if not prefix in transport_list_registry:
136
140
        register_transport_proto(prefix)
137
 
    transport_list_registry.register_lazy_transport_provider(prefix, module, classname)
138
 
 
139
 
 
140
 
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
 
141
    transport_list_registry.register_lazy_transport_provider(
 
142
        prefix, module, classname)
 
143
 
 
144
 
 
145
def register_transport(prefix, klass):
141
146
    if not prefix in transport_list_registry:
142
147
        register_transport_proto(prefix)
143
148
    transport_list_registry.register_transport_provider(prefix, klass)
282
287
        self.transport.append_bytes(self.relpath, bytes)
283
288
 
284
289
 
 
290
class TransportHooks(hooks.Hooks):
 
291
    """Mapping of hook names to registered callbacks for transport hooks"""
 
292
    def __init__(self):
 
293
        super(TransportHooks, self).__init__()
 
294
        self.add_hook("post_connect",
 
295
            "Called after a new connection is established or a reconnect "
 
296
            "occurs. The sole argument passed is either the connected "
 
297
            "transport or smart medium instance.", (2, 5))
 
298
 
 
299
 
285
300
class Transport(object):
286
301
    """This class encapsulates methods for retrieving or putting a file
287
302
    from/to a storage location.
306
321
    #       where the biggest benefit between combining reads and
307
322
    #       and seeking is. Consider a runtime auto-tune.
308
323
    _bytes_to_read_before_seek = 0
 
324
    
 
325
    hooks = TransportHooks()
309
326
 
310
327
    def __init__(self, base):
311
328
        super(Transport, self).__init__()
312
329
        self.base = base
313
 
        self._segment_parameters = urlutils.split_segment_parameters(
314
 
            base.rstrip("/"))[1]
 
330
        (self._raw_base, self._segment_parameters) = (
 
331
            urlutils.split_segment_parameters(base))
315
332
 
316
333
    def _translate_error(self, e, path, raise_generic=True):
317
334
        """Translate an IOError or OSError into an appropriate bzr error.
322
339
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
323
340
                raise errors.NoSuchFile(path, extra=e)
324
341
            elif e.errno == errno.EINVAL:
325
 
                mutter("EINVAL returned on path %s: %s" % (path, e))
 
342
                mutter("EINVAL returned on path %s: %r" % (path, e))
326
343
                raise errors.NoSuchFile(path, extra=e)
327
344
            # I would rather use errno.EFOO, but there doesn't seem to be
328
345
            # any matching for 267
348
365
        """
349
366
        raise NotImplementedError(self.clone)
350
367
 
351
 
    def create_prefix(self):
 
368
    def create_prefix(self, mode=None):
352
369
        """Create all the directories leading down to self.base."""
353
370
        cur_transport = self
354
371
        needed = [cur_transport]
360
377
                    "Failed to create path prefix for %s."
361
378
                    % cur_transport.base)
362
379
            try:
363
 
                new_transport.mkdir('.')
 
380
                new_transport.mkdir('.', mode=mode)
364
381
            except errors.NoSuchFile:
365
382
                needed.append(new_transport)
366
383
                cur_transport = new_transport
371
388
        # Now we only need to create child directories
372
389
        while needed:
373
390
            cur_transport = needed.pop()
374
 
            cur_transport.ensure_base()
 
391
            cur_transport.ensure_base(mode=mode)
375
392
 
376
 
    def ensure_base(self):
 
393
    def ensure_base(self, mode=None):
377
394
        """Ensure that the directory this transport references exists.
378
395
 
379
396
        This will create a directory if it doesn't exist.
383
400
        # than permission". We attempt to create the directory, and just
384
401
        # suppress FileExists and PermissionDenied (for Windows) exceptions.
385
402
        try:
386
 
            self.mkdir('.')
 
403
            self.mkdir('.', mode=mode)
387
404
        except (errors.FileExists, errors.PermissionDenied):
388
405
            return False
389
406
        else:
416
433
        """
417
434
        return self._segment_parameters
418
435
 
 
436
    def set_segment_parameter(self, name, value):
 
437
        """Set a segment parameter.
 
438
 
 
439
        :param name: Segment parameter name (urlencoded string)
 
440
        :param value: Segment parameter value (urlencoded string)
 
441
        """
 
442
        if value is None:
 
443
            try:
 
444
                del self._segment_parameters[name]
 
445
            except KeyError:
 
446
                pass
 
447
        else:
 
448
            self._segment_parameters[name] = value
 
449
        self.base = urlutils.join_segment_parameters(
 
450
            self._raw_base, self._segment_parameters)
 
451
 
419
452
    def _pump(self, from_file, to_file):
420
453
        """Most children will need to copy from one file-like
421
454
        object or string to another one.
840
873
            yield self.get(relpath)
841
874
            count += 1
842
875
 
843
 
    def put_bytes(self, relpath, bytes, mode=None):
 
876
    def put_bytes(self, relpath, raw_bytes, mode=None):
844
877
        """Atomically put the supplied bytes into the given location.
845
878
 
846
879
        :param relpath: The location to put the contents, relative to the
847
880
            transport base.
848
 
        :param bytes: A bytestring of data.
 
881
        :param raw_bytes: A bytestring of data.
849
882
        :param mode: Create the file with the given mode.
850
883
        :return: None
851
884
        """
852
 
        if not isinstance(bytes, str):
853
 
            raise AssertionError(
854
 
                'bytes must be a plain string, not %s' % type(bytes))
855
 
        return self.put_file(relpath, StringIO(bytes), mode=mode)
 
885
        if not isinstance(raw_bytes, str):
 
886
            raise TypeError(
 
887
                'raw_bytes must be a plain string, not %s' % type(raw_bytes))
 
888
        return self.put_file(relpath, StringIO(raw_bytes), mode=mode)
856
889
 
857
 
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
 
890
    def put_bytes_non_atomic(self, relpath, raw_bytes, mode=None,
858
891
                             create_parent_dir=False,
859
892
                             dir_mode=None):
860
893
        """Copy the string into the target location.
863
896
        Transport.put_bytes_non_atomic for more information.
864
897
 
865
898
        :param relpath: The remote location to put the contents.
866
 
        :param bytes:   A string object containing the raw bytes to write into
867
 
                        the target file.
 
899
        :param raw_bytes:   A string object containing the raw bytes to write
 
900
                        into the target file.
868
901
        :param mode:    Possible access permissions for new file.
869
902
                        None means do not set remote permissions.
870
903
        :param create_parent_dir: If we cannot create the target file because
872
905
                        create it, and then try again.
873
906
        :param dir_mode: Possible access permissions for new directories.
874
907
        """
875
 
        if not isinstance(bytes, str):
876
 
            raise AssertionError(
877
 
                'bytes must be a plain string, not %s' % type(bytes))
878
 
        self.put_file_non_atomic(relpath, StringIO(bytes), mode=mode,
 
908
        if not isinstance(raw_bytes, str):
 
909
            raise TypeError(
 
910
                'raw_bytes must be a plain string, not %s' % type(raw_bytes))
 
911
        self.put_file_non_atomic(relpath, StringIO(raw_bytes), mode=mode,
879
912
                                 create_parent_dir=create_parent_dir,
880
913
                                 dir_mode=dir_mode)
881
914
 
1399
1432
 
1400
1433
        :return: The corresponding URL.
1401
1434
        """
1402
 
        netloc = urllib.quote(host)
 
1435
        netloc = urlutils.quote(host)
1403
1436
        if user is not None:
1404
1437
            # Note that we don't put the password back even if we
1405
1438
            # have one so that it doesn't get accidentally
1406
1439
            # exposed.
1407
 
            netloc = '%s@%s' % (urllib.quote(user), netloc)
 
1440
            netloc = '%s@%s' % (urlutils.quote(user), netloc)
1408
1441
        if port is not None:
1409
1442
            netloc = '%s:%d' % (netloc, port)
1410
1443
        path = urlutils.escape(path)
1479
1512
        """
1480
1513
        self._shared_connection.connection = connection
1481
1514
        self._shared_connection.credentials = credentials
 
1515
        for hook in self.hooks["post_connect"]:
 
1516
            hook(self)
1482
1517
 
1483
1518
    def _get_connection(self):
1484
1519
        """Returns the transport specific connection object."""
1748
1783
                 help="Read-only access of branches exported on the web.")
1749
1784
register_transport_proto('https://',
1750
1785
            help="Read-only access of branches exported on the web using SSL.")
1751
 
# The default http implementation is urllib, but https is pycurl if available
 
1786
# The default http implementation is urllib
1752
1787
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl',
1753
1788
                        'PyCurlTransport')
1754
1789
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1755
1790
                        'HttpTransport_urllib')
 
1791
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl',
 
1792
                        'PyCurlTransport')
1756
1793
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1757
1794
                        'HttpTransport_urllib')
1758
 
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl',
1759
 
                        'PyCurlTransport')
1760
1795
 
1761
1796
register_transport_proto('ftp://', help="Access using passive FTP.")
1762
1797
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')