1
# Copyright (C) 2005-2011 Canonical Ltd
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
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
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)
140
def register_transport(prefix, klass, override=DEPRECATED_PARAMETER):
141
transport_list_registry.register_lazy_transport_provider(
142
prefix, module, classname)
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)
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))
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
325
hooks = TransportHooks()
310
327
def __init__(self, base):
311
328
super(Transport, self).__init__()
313
self._segment_parameters = urlutils.split_segment_parameters(
330
(self._raw_base, self._segment_parameters) = (
331
urlutils.split_segment_parameters(base))
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
360
377
"Failed to create path prefix for %s."
361
378
% cur_transport.base)
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
373
390
cur_transport = needed.pop()
374
cur_transport.ensure_base()
391
cur_transport.ensure_base(mode=mode)
376
def ensure_base(self):
393
def ensure_base(self, mode=None):
377
394
"""Ensure that the directory this transport references exists.
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.
403
self.mkdir('.', mode=mode)
387
404
except (errors.FileExists, errors.PermissionDenied):
417
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)
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)
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.
846
879
:param relpath: The location to put the contents, relative to the
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.
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):
887
'raw_bytes must be a plain string, not %s' % type(raw_bytes))
888
return self.put_file(relpath, StringIO(raw_bytes), mode=mode)
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,
860
893
"""Copy the string into the target location.
863
896
Transport.put_bytes_non_atomic for more information.
865
898
:param relpath: The remote location to put the contents.
866
:param bytes: A string object containing the raw bytes to write into
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.
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):
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)
1400
1433
:return: The corresponding URL.
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
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)
1480
1513
self._shared_connection.connection = connection
1481
1514
self._shared_connection.credentials = credentials
1515
for hook in self.hooks["post_connect"]:
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',
1756
1793
register_lazy_transport('https://', 'bzrlib.transport.http._urllib',
1757
1794
'HttpTransport_urllib')
1758
register_lazy_transport('https://', 'bzrlib.transport.http._pycurl',
1761
1796
register_transport_proto('ftp://', help="Access using passive FTP.")
1762
1797
register_lazy_transport('ftp://', 'bzrlib.transport.ftp', 'FtpTransport')