287
267
self.transport.append_bytes(self.relpath, bytes)
290
class TransportHooks(hooks.Hooks):
291
"""Mapping of hook names to registered callbacks for transport hooks"""
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))
300
270
class Transport(object):
301
271
"""This class encapsulates methods for retrieving or putting a file
302
272
from/to a storage location.
304
274
Most functions have a _multi variant, which allows you to queue up
305
multiple requests. They generally have a dumb base implementation
275
multiple requests. They generally have a dumb base implementation
306
276
which just iterates over the arguments, but smart Transport
307
277
implementations can do pipelining.
308
278
In general implementations should support having a generator or a list
361
324
def clone(self, offset=None):
362
325
"""Return a new Transport object, cloned from the current location,
363
using a subdirectory or parent directory. This allows connections
326
using a subdirectory or parent directory. This allows connections
364
327
to be pooled, rather than a new one needed for each subdir.
366
329
raise NotImplementedError(self.clone)
368
def create_prefix(self, mode=None):
369
"""Create all the directories leading down to self.base."""
371
needed = [cur_transport]
372
# Recurse upwards until we can create a directory successfully
374
new_transport = cur_transport.clone('..')
375
if new_transport.base == cur_transport.base:
376
raise errors.BzrCommandError(
377
"Failed to create path prefix for %s."
378
% cur_transport.base)
380
new_transport.mkdir('.', mode=mode)
381
except errors.NoSuchFile:
382
needed.append(new_transport)
383
cur_transport = new_transport
384
except errors.FileExists:
388
# Now we only need to create child directories
390
cur_transport = needed.pop()
391
cur_transport.ensure_base(mode=mode)
393
def ensure_base(self, mode=None):
331
def ensure_base(self):
394
332
"""Ensure that the directory this transport references exists.
396
334
This will create a directory if it doesn't exist.
429
367
raise NotImplementedError(self.external_url)
431
def get_segment_parameters(self):
432
"""Return the segment parameters for the top segment of the URL.
434
return self._segment_parameters
436
def set_segment_parameter(self, name, value):
437
"""Set a segment parameter.
439
:param name: Segment parameter name (urlencoded string)
440
:param value: Segment parameter value (urlencoded string)
444
del self._segment_parameters[name]
448
self._segment_parameters[name] = value
449
self.base = urlutils.join_segment_parameters(
450
self._raw_base, self._segment_parameters)
452
369
def _pump(self, from_file, to_file):
453
"""Most children will need to copy from one file-like
370
"""Most children will need to copy from one file-like
454
371
object or string to another one.
455
372
This just gives them something easy to call.
520
425
raise NotImplementedError(self.abspath)
427
def _combine_paths(self, base_path, relpath):
428
"""Transform a Transport-relative path to a remote absolute path.
430
This does not handle substitution of ~ but does handle '..' and '.'
435
t._combine_paths('/home/sarah', 'project/foo')
436
=> '/home/sarah/project/foo'
437
t._combine_paths('/home/sarah', '../../etc')
439
t._combine_paths('/home/sarah', '/etc')
442
:param base_path: urlencoded path for the transport root; typically a
443
URL but need not contain scheme/host/etc.
444
:param relpath: relative url string for relative part of remote path.
445
:return: urlencoded string for final path.
447
if not isinstance(relpath, str):
448
raise errors.InvalidURL(relpath)
449
if relpath.startswith('/'):
452
base_parts = base_path.split('/')
453
if len(base_parts) > 0 and base_parts[-1] == '':
454
base_parts = base_parts[:-1]
455
for p in relpath.split('/'):
457
if len(base_parts) == 0:
458
# In most filesystems, a request for the parent
459
# of root, just returns root.
466
path = '/'.join(base_parts)
467
if not path.startswith('/'):
522
471
def recommended_page_size(self):
523
472
"""Return the recommended page size for this transport.
525
474
This is potentially different for every path in a given namespace.
526
For example, local transports might use an operating system call to
475
For example, local transports might use an operating system call to
527
476
get the block size for a given path, which can vary due to mount
710
673
# Cache the results, but only until they have been fulfilled
713
for c_offset in coalesced:
714
# TODO: jam 20060724 it might be faster to not issue seek if
715
# we are already at the right location. This should be
717
fp.seek(c_offset.start)
718
data = fp.read(c_offset.length)
719
if len(data) < c_offset.length:
720
raise errors.ShortReadvError(relpath, c_offset.start,
721
c_offset.length, actual=len(data))
722
for suboffset, subsize in c_offset.ranges:
723
key = (c_offset.start+suboffset, subsize)
724
data_map[key] = data[suboffset:suboffset+subsize]
675
for c_offset in coalesced:
676
# TODO: jam 20060724 it might be faster to not issue seek if
677
# we are already at the right location. This should be
679
fp.seek(c_offset.start)
680
data = fp.read(c_offset.length)
681
if len(data) < c_offset.length:
682
raise errors.ShortReadvError(relpath, c_offset.start,
683
c_offset.length, actual=len(data))
684
for suboffset, subsize in c_offset.ranges:
685
key = (c_offset.start+suboffset, subsize)
686
data_map[key] = data[suboffset:suboffset+subsize]
726
# Now that we've read some data, see if we can yield anything back
727
while cur_offset_and_size in data_map:
728
this_data = data_map.pop(cur_offset_and_size)
729
this_offset = cur_offset_and_size[0]
731
cur_offset_and_size = offset_stack.next()
732
except StopIteration:
734
cur_offset_and_size = None
735
yield this_offset, this_data
688
# Now that we've read some data, see if we can yield anything back
689
while cur_offset_and_size in data_map:
690
this_data = data_map.pop(cur_offset_and_size)
691
yield cur_offset_and_size[0], this_data
692
cur_offset_and_size = offset_stack.next()
739
694
def _sort_expand_and_combine(self, offsets, upper_limit):
740
695
"""Helper for readv.
803
758
into a single large request, while retaining the original
805
760
Turns [(15, 10), (25, 10)] => [(15, 20, [(0, 10), (10, 10)])]
806
Note that overlapping requests are not permitted. (So [(15, 10), (20,
807
10)] will raise a ValueError.) This is because the data we access never
808
overlaps, and it allows callers to trust that we only need any byte of
809
data for 1 request (so nothing needs to be buffered to fulfill a second
812
762
:param offsets: A list of (start, length) pairs
813
764
:param limit: Only combine a maximum of this many pairs Some transports
814
765
penalize multiple reads more than others, and sometimes it is
815
766
better to return early.
817
769
:param fudge_factor: All transports have some level of 'it is
818
better to read some more data and throw it away rather
770
better to read some more data and throw it away rather
819
771
than seek', so collapse if we are 'close enough'
820
773
:param max_size: Create coalesced offsets no bigger than this size.
821
774
When a single offset is bigger than 'max_size', it will keep
822
775
its size and be alone in the coalesced offset.
823
776
0 means no maximum size.
824
:return: return a list of _CoalescedOffset objects, which have members
825
for where to start, how much to read, and how to split those chunks
778
:return: yield _CoalescedOffset objects, which have members for where
779
to start, how much to read, and how to split those
829
783
cur = _CoalescedOffset(None, None, [])
830
coalesced_offsets = []
833
# 'unlimited', but we actually take this to mean 100MB buffer limit
834
max_size = 100*1024*1024
836
785
for start, size in offsets:
837
786
end = start + size
840
789
and start >= cur.start
841
790
and (limit <= 0 or len(cur.ranges) < limit)
842
791
and (max_size <= 0 or end - cur.start <= max_size)):
844
raise ValueError('Overlapping range not allowed:'
845
' last range ended at %s, new one starts at %s'
847
792
cur.length = end - cur.start
848
793
cur.ranges.append((start-cur.start, size))
850
795
if cur.start is not None:
851
coalesced_offsets.append(cur)
852
797
cur = _CoalescedOffset(start, size, [(0, size)])
855
800
if cur.start is not None:
856
coalesced_offsets.append(cur)
857
return coalesced_offsets
859
805
def get_multi(self, relpaths, pb=None):
860
806
"""Get a list of file-like objects, one for each entry in relpaths.
862
808
:param relpaths: A list of relative paths.
863
:param pb: An optional ProgressTask for indicating percent done.
809
:param pb: An optional ProgressBar for indicating percent done.
864
810
:return: A list or generator of file-like objects
866
812
# TODO: Consider having this actually buffer the requests,
873
819
yield self.get(relpath)
876
def put_bytes(self, relpath, raw_bytes, mode=None):
822
def put_bytes(self, relpath, bytes, mode=None):
877
823
"""Atomically put the supplied bytes into the given location.
879
825
:param relpath: The location to put the contents, relative to the
881
:param raw_bytes: A bytestring of data.
827
:param bytes: A bytestring of data.
882
828
:param mode: Create the file with the given mode.
885
if not isinstance(raw_bytes, str):
887
'raw_bytes must be a plain string, not %s' % type(raw_bytes))
888
return self.put_file(relpath, StringIO(raw_bytes), mode=mode)
831
if not isinstance(bytes, str):
832
raise AssertionError(
833
'bytes must be a plain string, not %s' % type(bytes))
834
return self.put_file(relpath, StringIO(bytes), mode=mode)
890
def put_bytes_non_atomic(self, relpath, raw_bytes, mode=None,
836
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
891
837
create_parent_dir=False,
893
839
"""Copy the string into the target location.
895
This function is not strictly safe to use. See
841
This function is not strictly safe to use. See
896
842
Transport.put_bytes_non_atomic for more information.
898
844
:param relpath: The remote location to put the contents.
899
:param raw_bytes: A string object containing the raw bytes to write
900
into the target file.
845
:param bytes: A string object containing the raw bytes to write into
901
847
:param mode: Possible access permissions for new file.
902
848
None means do not set remote permissions.
903
849
:param create_parent_dir: If we cannot create the target file because
1029
975
the supplied location.
1031
977
:param files: A set of (path, f) entries
1032
:param pb: An optional ProgressTask for indicating percent done.
978
:param pb: An optional ProgressBar for indicating percent done.
1034
980
return self._iterate_over(files, self.append_file, pb, 'append', expand=True)
1036
982
def copy(self, rel_from, rel_to):
1037
983
"""Copy the item at rel_from to the location at rel_to.
1039
Override this for efficiency if a specific transport can do it
985
Override this for efficiency if a specific transport can do it
1040
986
faster than this default implementation.
1042
988
self.put_file(rel_to, self.get(rel_from))
1044
990
def copy_multi(self, relpaths, pb=None):
1045
991
"""Copy a bunch of entries.
1047
993
:param relpaths: A list of tuples of the form [(from, to), (from, to),...]
1049
995
# This is the non-pipelined implementation, so that
1067
1013
def copy_tree(self, from_relpath, to_relpath):
1068
1014
"""Copy a subtree from one relpath to another.
1070
If a faster implementation is available, specific transports should
1016
If a faster implementation is available, specific transports should
1073
1019
source = self.clone(from_relpath)
1020
self.mkdir(to_relpath)
1074
1021
target = self.clone(to_relpath)
1076
# create target directory with the same rwx bits as source.
1077
# use mask to ensure that bits other than rwx are ignored.
1078
stat = self.stat(from_relpath)
1079
target.mkdir('.', stat.st_mode & 0777)
1080
source.copy_tree_to_transport(target)
1082
def copy_tree_to_transport(self, to_transport):
1083
"""Copy a subtree from one transport to another.
1085
self.base is used as the source tree root, and to_transport.base
1086
is used as the target. to_transport.base must exist (and be a
1090
1023
directories = ['.']
1091
1024
while directories:
1092
1025
dir = directories.pop()
1094
to_transport.mkdir(dir)
1095
for path in self.list_dir(dir):
1028
for path in source.list_dir(dir):
1096
1029
path = dir + '/' + path
1097
stat = self.stat(path)
1030
stat = source.stat(path)
1098
1031
if S_ISDIR(stat.st_mode):
1099
1032
directories.append(path)
1101
1034
files.append(path)
1102
self.copy_to(files, to_transport)
1035
source.copy_to(files, target)
1104
1037
def rename(self, rel_from, rel_to):
1105
1038
"""Rename a file or directory.
1360
1259
if not base.endswith('/'):
1362
self._parsed_url = self._split_url(base)
1262
self._user, self._password,
1263
self._host, self._port,
1264
self._path) = self._split_url(base)
1363
1265
if _from_transport is not None:
1364
1266
# Copy the password as it does not appear in base and will be lost
1365
1267
# otherwise. It can appear in the _split_url above if the user
1366
1268
# provided it on the command line. Otherwise, daughter classes will
1367
1269
# prompt the user for one when appropriate.
1368
self._parsed_url.password = _from_transport._parsed_url.password
1369
self._parsed_url.quoted_password = (
1370
_from_transport._parsed_url.quoted_password)
1270
self._password = _from_transport._password
1372
base = str(self._parsed_url)
1272
base = self._unsplit_url(self._scheme,
1273
self._user, self._password,
1274
self._host, self._port,
1374
1277
super(ConnectedTransport, self).__init__(base)
1375
1278
if _from_transport is None:
1416
1295
def _split_url(url):
1417
return urlutils.URL.from_string(url)
1297
Extract the server address, the credentials and the path from the url.
1299
user, password, host and path should be quoted if they contain reserved
1302
:param url: an quoted url
1304
:return: (scheme, user, password, host, port, path) tuple, all fields
1307
if isinstance(url, unicode):
1308
raise errors.InvalidURL('should be ascii:\n%r' % url)
1309
url = url.encode('utf-8')
1310
(scheme, netloc, path, params,
1311
query, fragment) = urlparse.urlparse(url, allow_fragments=False)
1312
user = password = host = port = None
1314
user, host = netloc.rsplit('@', 1)
1316
user, password = user.split(':', 1)
1317
password = urllib.unquote(password)
1318
user = urllib.unquote(user)
1323
host, port = host.rsplit(':', 1)
1327
raise errors.InvalidURL('invalid port number %s in url:\n%s' %
1330
raise errors.InvalidURL('Host empty in: %s' % url)
1332
host = urllib.unquote(host)
1333
path = urllib.unquote(path)
1335
return (scheme, user, password, host, port, path)
1420
1338
def _unsplit_url(scheme, user, password, host, port, path):
1421
"""Build the full URL for the given already URL encoded path.
1340
Build the full URL for the given already URL encoded path.
1423
1342
user, password, host and path will be quoted if they contain reserved
1426
1345
:param scheme: protocol
1427
1347
:param user: login
1428
1349
:param password: associated password
1429
1351
:param host: the server address
1430
1353
:param port: the associated port
1431
1355
:param path: the absolute path on the server
1433
1357
:return: The corresponding URL.
1435
netloc = urlutils.quote(host)
1359
netloc = urllib.quote(host)
1436
1360
if user is not None:
1437
1361
# Note that we don't put the password back even if we
1438
1362
# have one so that it doesn't get accidentally
1440
netloc = '%s@%s' % (urlutils.quote(user), netloc)
1364
netloc = '%s@%s' % (urllib.quote(user), netloc)
1441
1365
if port is not None:
1442
1366
netloc = '%s:%d' % (netloc, port)
1443
path = urlutils.escape(path)
1367
path = urllib.quote(path)
1444
1368
return urlparse.urlunparse((scheme, netloc, path, None, None, None))
1446
1370
def relpath(self, abspath):
1447
1371
"""Return the local path portion from a given absolute path"""
1448
parsed_url = self._split_url(abspath)
1372
scheme, user, password, host, port, path = self._split_url(abspath)
1450
if parsed_url.scheme != self._parsed_url.scheme:
1374
if (scheme != self._scheme):
1451
1375
error.append('scheme mismatch')
1452
if parsed_url.user != self._parsed_url.user:
1376
if (user != self._user):
1453
1377
error.append('user name mismatch')
1454
if parsed_url.host != self._parsed_url.host:
1378
if (host != self._host):
1455
1379
error.append('host mismatch')
1456
if parsed_url.port != self._parsed_url.port:
1380
if (port != self._port):
1457
1381
error.append('port mismatch')
1458
if (not (parsed_url.path == self._parsed_url.path[:-1] or
1459
parsed_url.path.startswith(self._parsed_url.path))):
1382
if not (path == self._path[:-1] or path.startswith(self._path)):
1460
1383
error.append('path mismatch')
1462
1385
extra = ', '.join(error)
1463
1386
raise errors.PathNotChild(abspath, self.base, extra=extra)
1464
pl = len(self._parsed_url.path)
1465
return parsed_url.path[pl:].strip('/')
1387
pl = len(self._path)
1388
return path[pl:].strip('/')
1467
1390
def abspath(self, relpath):
1468
1391
"""Return the full url to the given relative path.
1470
1393
:param relpath: the relative path urlencoded
1472
1395
:returns: the Unicode version of the absolute path for relpath.
1474
return str(self._parsed_url.clone(relpath))
1397
relative = urlutils.unescape(relpath).encode('utf-8')
1398
path = self._combine_paths(self._path, relative)
1399
return self._unsplit_url(self._scheme, self._user, self._password,
1400
self._host, self._port,
1476
1403
def _remote_path(self, relpath):
1477
1404
"""Return the absolute path part of the url to the given relative path.
1578
1505
transport = self.__class__(other_base, _from_transport=self)
1579
1506
return transport
1581
def disconnect(self):
1582
"""Disconnect the transport.
1584
If and when required the transport willl reconnect automatically.
1586
raise NotImplementedError(self.disconnect)
1589
def location_to_url(location):
1590
"""Determine a fully qualified URL from a location string.
1592
This will try to interpret location as both a URL and a directory path. It
1593
will also lookup the location in directories.
1595
:param location: Unicode or byte string object with a location
1596
:raise InvalidURL: If the location is already a URL, but not valid.
1597
:return: Byte string with resulting URL
1509
# We try to recognize an url lazily (ignoring user, password, etc)
1510
_urlRE = re.compile(r'^(?P<proto>[^:/\\]+)://(?P<rest>.*)$')
1512
def get_transport(base, possible_transports=None):
1513
"""Open a transport to access a URL or directory.
1515
:param base: either a URL or a directory name.
1517
:param transports: optional reusable transports list. If not None, created
1518
transports will be added to the list.
1520
:return: A new transport optionally sharing its connection with one of
1521
possible_transports.
1599
if not isinstance(location, basestring):
1600
raise AssertionError("location not a byte or unicode string")
1601
1526
from bzrlib.directory_service import directories
1602
location = directories.dereference(location)
1527
base = directories.dereference(base)
1529
def convert_path_to_url(base, error_str):
1530
m = _urlRE.match(base)
1532
# This looks like a URL, but we weren't able to
1533
# instantiate it as such raise an appropriate error
1534
# FIXME: we have a 'error_str' unused and we use last_err below
1535
raise errors.UnsupportedProtocol(base, last_err)
1536
# This doesn't look like a protocol, consider it a local path
1537
new_base = urlutils.local_path_to_url(base)
1538
# mutter('converting os path %r => url %s', base, new_base)
1604
1541
# Catch any URLs which are passing Unicode rather than ASCII
1606
location = location.encode('ascii')
1543
base = base.encode('ascii')
1607
1544
except UnicodeError:
1608
if urlutils.is_url(location):
1609
raise errors.InvalidURL(path=location,
1610
extra='URLs must be properly escaped')
1611
location = urlutils.local_path_to_url(location)
1613
if location.startswith("file:") and not location.startswith("file://"):
1614
return urlutils.join(urlutils.local_path_to_url("."), location[5:])
1616
if not urlutils.is_url(location):
1617
return urlutils.local_path_to_url(location)
1622
def get_transport_from_path(path, possible_transports=None):
1623
"""Open a transport for a local path.
1625
:param path: Local path as byte or unicode string
1626
:return: Transport object for path
1628
return get_transport_from_url(urlutils.local_path_to_url(path),
1629
possible_transports)
1632
def get_transport_from_url(url, possible_transports=None):
1633
"""Open a transport to access a URL.
1636
:param transports: optional reusable transports list. If not None, created
1637
transports will be added to the list.
1639
:return: A new transport optionally sharing its connection with one of
1640
possible_transports.
1545
# Only local paths can be Unicode
1546
base = convert_path_to_url(base,
1547
'URLs must be properly escaped (protocol: %s)')
1642
1549
transport = None
1643
1550
if possible_transports is not None:
1644
1551
for t in possible_transports:
1645
t_same_connection = t._reuse_for(url)
1552
t_same_connection = t._reuse_for(base)
1646
1553
if t_same_connection is not None:
1647
1554
# Add only new transports
1648
1555
if t_same_connection not in possible_transports:
1649
1556
possible_transports.append(t_same_connection)
1650
1557
return t_same_connection
1653
for proto, factory_list in transport_list_registry.items():
1654
if proto is not None and url.startswith(proto):
1655
transport, last_err = _try_transport_factories(url, factory_list)
1559
for proto, factory_list in transport_list_registry.iteritems():
1560
if proto is not None and base.startswith(proto):
1561
transport, last_err = _try_transport_factories(base, factory_list)
1657
1563
if possible_transports is not None:
1658
1564
if transport in possible_transports:
1659
1565
raise AssertionError()
1660
1566
possible_transports.append(transport)
1661
1567
return transport
1662
if not urlutils.is_url(url):
1663
raise errors.InvalidURL(path=url)
1664
raise errors.UnsupportedProtocol(url, last_err)
1667
def get_transport(base, possible_transports=None):
1668
"""Open a transport to access a URL or directory.
1670
:param base: either a URL or a directory name.
1672
:param transports: optional reusable transports list. If not None, created
1673
transports will be added to the list.
1675
:return: A new transport optionally sharing its connection with one of
1676
possible_transports.
1680
return get_transport_from_url(location_to_url(base), possible_transports)
1569
# We tried all the different protocols, now try one last time
1570
# as a local protocol
1571
base = convert_path_to_url(base, 'Unsupported protocol: %s')
1573
# The default handler is the filesystem handler, stored as protocol None
1574
factory_list = transport_list_registry.get(None)
1575
transport, last_err = _try_transport_factories(base, factory_list)
1683
1580
def _try_transport_factories(base, factory_list):
1738
1635
class Server(object):
1739
1636
"""A Transport Server.
1741
The Server interface provides a server for a given transport type.
1638
The Server interface provides a server for a given transport. We use
1639
these servers as loopback testing tools. For any given transport the
1640
Servers it provides must either allow writing, or serve the contents
1641
of os.getcwdu() at the time setUp is called.
1643
Note that these are real servers - they must implement all the things
1644
that we want bzr transports to take advantage of.
1744
def start_server(self):
1745
1648
"""Setup the server to service requests."""
1747
def stop_server(self):
1748
1651
"""Remove the server and cleanup any resources it owns."""
1654
"""Return a url for this server.
1656
If the transport does not represent a disk directory (i.e. it is
1657
a database like svn, or a memory only transport, it should return
1658
a connection to a newly established resource for this Server.
1659
Otherwise it should return a url that will provide access to the path
1660
that was os.getcwdu() when setUp() was called.
1662
Subsequent calls will return the same resource.
1664
raise NotImplementedError
1666
def get_bogus_url(self):
1667
"""Return a url for this protocol, that will fail to connect.
1669
This may raise NotImplementedError to indicate that this server cannot
1672
raise NotImplementedError
1751
1675
# None is the default transport, for things with no url scheme
1752
1676
register_transport_proto('file://',
1753
1677
help="Access using the standard filesystem (default)")
1754
1678
register_lazy_transport('file://', 'bzrlib.transport.local', 'LocalTransport')
1679
transport_list_registry.set_default_transport("file://")
1756
1681
register_transport_proto('sftp://',
1757
1682
help="Access using SFTP (most SSH servers provide SFTP).",
1783
1708
help="Read-only access of branches exported on the web.")
1784
1709
register_transport_proto('https://',
1785
1710
help="Read-only access of branches exported on the web using SSL.")
1786
# The default http implementation is urllib
1711
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1712
'HttpTransport_urllib')
1713
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1714
'HttpTransport_urllib')
1787
1715
register_lazy_transport('http://', 'bzrlib.transport.http._pycurl',
1788
1716
'PyCurlTransport')
1789
register_lazy_transport('http://', 'bzrlib.transport.http._urllib',
1790
'HttpTransport_urllib')
1791
1717
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl',
1792
1718
'PyCurlTransport')
1793
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1794
'HttpTransport_urllib')
1796
1720
register_transport_proto('ftp://', help="Access using passive FTP.")
1797
1721
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1798
1722
register_transport_proto('aftp://', help="Access using active FTP.")
1799
1723
register_lazy_transport('aftp://', 'bzrlib.transport.ftp', 'FtpTransport')
1800
register_transport_proto('gio+', help="Access using any GIO supported protocols.")
1801
register_lazy_transport('gio+', 'bzrlib.transport.gio_transport', 'GioTransport')
1804
# Default to trying GSSAPI authentication (if the kerberos module is
1725
# Default to trying GSSAPI authentication (if the kerberos module is available)
1806
1726
register_transport_proto('ftp+gssapi://', register_netloc=True)
1727
register_lazy_transport('ftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1728
'GSSAPIFtpTransport')
1807
1729
register_transport_proto('aftp+gssapi://', register_netloc=True)
1730
register_lazy_transport('aftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1731
'GSSAPIFtpTransport')
1808
1732
register_transport_proto('ftp+nogssapi://', register_netloc=True)
1809
1733
register_transport_proto('aftp+nogssapi://', register_netloc=True)
1810
register_lazy_transport('ftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1811
'GSSAPIFtpTransport')
1812
register_lazy_transport('aftp+gssapi://', 'bzrlib.transport.ftp._gssapi',
1813
'GSSAPIFtpTransport')
1814
register_lazy_transport('ftp://', 'bzrlib.transport.ftp._gssapi',
1815
'GSSAPIFtpTransport')
1816
register_lazy_transport('aftp://', 'bzrlib.transport.ftp._gssapi',
1817
'GSSAPIFtpTransport')
1818
register_lazy_transport('ftp+nogssapi://', 'bzrlib.transport.ftp',
1735
register_lazy_transport('ftp://', 'bzrlib.transport.ftp._gssapi',
1736
'GSSAPIFtpTransport')
1737
register_lazy_transport('aftp://', 'bzrlib.transport.ftp._gssapi',
1738
'GSSAPIFtpTransport')
1739
register_lazy_transport('ftp+nogssapi://', 'bzrlib.transport.ftp',
1819
1740
'FtpTransport')
1820
register_lazy_transport('aftp+nogssapi://', 'bzrlib.transport.ftp',
1741
register_lazy_transport('aftp+nogssapi://', 'bzrlib.transport.ftp',
1821
1742
'FtpTransport')
1823
1744
register_transport_proto('memory://')
1824
1745
register_lazy_transport('memory://', 'bzrlib.transport.memory',
1825
1746
'MemoryTransport')
1748
# chroots cannot be implicitly accessed, they must be explicitly created:
1749
register_transport_proto('chroot+')
1827
1751
register_transport_proto('readonly+',
1828
1752
# help="This modifier converts any transport to be readonly."