29
from cStringIO import StringIO
33
from bzrlib.lazy_import import lazy_import
34
lazy_import(globals(), """
30
36
from collections import deque
31
37
from copy import deepcopy
32
from cStringIO import StringIO
34
38
from stat import S_ISDIR
36
from unittest import TestSuite
166
169
except ValueError:
167
170
# TODO: Should this be ConnectionError?
168
raise errors.TransportError('%s: invalid port number' % port)
171
raise errors.TransportError(
172
'invalid port number %s in url:\n%s' % (port, url))
169
173
host = urllib.unquote(host)
171
175
path = urllib.unquote(path)
198
202
implementations can do pipelining.
199
203
In general implementations should support having a generator or a list
200
204
as an argument (ie always iterate, never index)
206
:ivar base: Base URL for the transport; should always end in a slash.
203
209
# implementations can override this if it is more efficient
221
227
This handles things like ENOENT, ENOTDIR, EEXIST, and EACCESS
223
if hasattr(e, 'errno'):
229
if getattr(e, 'errno', None) is not None:
224
230
if e.errno in (errno.ENOENT, errno.ENOTDIR):
225
231
raise errors.NoSuchFile(path, extra=e)
226
232
# I would rather use errno.EFOO, but there doesn't seem to be
260
266
assert not isinstance(from_file, basestring), \
261
267
'_pump should only be called on files not %s' % (type(from_file,))
262
pumpfile(from_file, to_file)
268
osutils.pumpfile(from_file, to_file)
264
270
def _get_total(self, multi):
265
271
"""Try to figure out how many entries are in multi,
305
311
def abspath(self, relpath):
306
312
"""Return the full url to the given relative path.
307
This can be supplied with a string or a list
309
XXX: Robert Collins 20051016 - is this really needed in the public
314
:param relpath: a string of a relative path
317
# XXX: Robert Collins 20051016 - is this really needed in the public
312
319
raise NotImplementedError(self.abspath)
321
def _combine_paths(self, base_path, relpath):
322
"""Transform a Transport-relative path to a remote absolute path.
324
This does not handle substitution of ~ but does handle '..' and '.'
329
t._combine_paths('/home/sarah', 'project/foo')
330
=> '/home/sarah/project/foo'
331
t._combine_paths('/home/sarah', '../../etc')
334
:param base_path: urlencoded path for the transport root; typically a
335
URL but need not contain scheme/host/etc.
336
:param relpath: relative url string for relative part of remote path.
337
:return: urlencoded string for final path.
339
# FIXME: share the common code across more transports; variants of
340
# this likely occur in http and sftp too.
342
# TODO: Also need to consider handling of ~, which might vary between
344
if not isinstance(relpath, str):
345
raise errors.InvalidURL("not a valid url: %r" % relpath)
346
if relpath.startswith('/'):
349
base_parts = base_path.split('/')
350
if len(base_parts) > 0 and base_parts[-1] == '':
351
base_parts = base_parts[:-1]
352
for p in relpath.split('/'):
354
if len(base_parts) == 0:
355
# In most filesystems, a request for the parent
356
# of root, just returns root.
363
path = '/'.join(base_parts)
314
366
def relpath(self, abspath):
315
367
"""Return the local path portion from a given absolute path.
342
394
Note that some transports MAY allow querying on directories, but this
343
395
is not part of the protocol. In other words, the results of
344
396
t.has("a_directory_name") are undefined.
346
400
raise NotImplementedError(self.has)
389
444
return self.get(relpath).read()
446
def get_smart_client(self):
447
"""Return a smart client for this transport if possible.
449
A smart client doesn't imply the presence of a smart server: it implies
450
that the smart protocol can be tunnelled via this transport.
452
:raises NoSmartServer: if no smart server client is available.
454
raise errors.NoSmartServer(self.base)
456
def get_smart_medium(self):
457
"""Return a smart client medium for this transport if possible.
459
A smart medium doesn't imply the presence of a smart server: it implies
460
that the smart protocol can be tunnelled via this transport.
462
:raises NoSmartMedium: if no smart server medium is available.
464
raise errors.NoSmartMedium(self)
391
466
def readv(self, relpath, offsets):
392
467
"""Get parts of the file at the given relative path.
400
475
fp = self.get(relpath)
401
return self._seek_and_read(fp, offsets)
476
return self._seek_and_read(fp, offsets, relpath)
403
def _seek_and_read(self, fp, offsets):
478
def _seek_and_read(self, fp, offsets, relpath='<unknown>'):
404
479
"""An implementation of readv that uses fp.seek and fp.read.
406
481
This uses _coalesce_offsets to issue larger reads and fewer seeks.
429
504
fp.seek(c_offset.start)
430
505
data = fp.read(c_offset.length)
506
if len(data) < c_offset.length:
507
raise errors.ShortReadvError(relpath, c_offset.start,
508
c_offset.length, actual=len(data))
431
509
for suboffset, subsize in c_offset.ranges:
432
510
key = (c_offset.start+suboffset, subsize)
433
511
data_map[key] = data[suboffset:suboffset+subsize]
526
604
return self.put_file(relpath, StringIO(bytes), mode=mode)
528
606
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
529
create_parent_dir=False):
607
create_parent_dir=False,
530
609
"""Copy the string into the target location.
532
611
This function is not strictly safe to use. See
540
619
:param create_parent_dir: If we cannot create the target file because
541
620
the parent directory does not exist, go ahead and
542
621
create it, and then try again.
622
:param dir_mode: Possible access permissions for new directories.
544
624
assert isinstance(bytes, str), \
545
625
'bytes must be a plain string, not %s' % type(bytes)
546
626
self.put_file_non_atomic(relpath, StringIO(bytes), mode=mode,
547
create_parent_dir=create_parent_dir)
627
create_parent_dir=create_parent_dir,
549
630
def put_file(self, relpath, f, mode=None):
550
631
"""Copy the file-like object into the location.
565
646
#raise NotImplementedError(self.put_file)
567
648
def put_file_non_atomic(self, relpath, f, mode=None,
568
create_parent_dir=False):
649
create_parent_dir=False,
569
651
"""Copy the file-like object into the target location.
571
653
This function is not strictly safe to use. It is only meant to
580
662
:param create_parent_dir: If we cannot create the target file because
581
663
the parent directory does not exist, go ahead and
582
664
create it, and then try again.
665
:param dir_mode: Possible access permissions for new directories.
584
667
# Default implementation just does an atomic put.
590
673
parent_dir = osutils.dirname(relpath)
592
self.mkdir(parent_dir)
675
self.mkdir(parent_dir, mode=dir_mode)
593
676
return self.put_file(relpath, f, mode=mode)
595
678
@deprecated_method(zero_eleven)
629
712
return self.append_file(relpath, f, mode=mode)
631
714
def append_file(self, relpath, f, mode=None):
632
"""Append the text in the file-like object to the supplied location.
634
returns the length of relpath before the content was written to it.
636
If the file does not exist, it is created with the supplied mode.
715
"""Append bytes from a file-like object to a file at relpath.
717
The file is created if it does not already exist.
719
:param f: a file-like object of the bytes to append.
720
:param mode: Unix mode for newly created files. This is not used for
723
:returns: the length of relpath before the content was written to it.
638
725
symbol_versioning.warn('Transport %s should implement append_file,'
639
726
' rather than implementing append() as of'
643
730
return self.append(relpath, f, mode=mode)
645
732
def append_bytes(self, relpath, bytes, mode=None):
646
"""Append the text in the string object to the supplied location.
648
returns the length of relpath before the content was written to it.
650
If the file does not exist, it is created with the supplied mode.
733
"""Append bytes to a file at relpath.
735
The file is created if it does not already exist.
738
:param f: a string of the bytes to append.
739
:param mode: Unix mode for newly created files. This is not used for
742
:returns: the length of relpath before the content was written to it.
652
744
assert isinstance(bytes, str), \
653
745
'bytes must be a plain string, not %s' % type(bytes)
843
935
WARNING: many transports do not support this, so trying avoid using
844
936
it if at all possible.
846
raise errors.TransportNotPossible("This transport has not "
938
raise errors.TransportNotPossible("Transport %r has not "
847
939
"implemented list_dir "
848
940
"(but must claim to be listable "
849
"to trigger this error).")
941
"to trigger this error)."
851
944
def lock_read(self, relpath):
852
945
"""Lock the given file for shared (read) access.
853
WARNING: many transports do not support this, so trying avoid using it
947
WARNING: many transports do not support this, so trying avoid using it.
948
These methods may be removed in the future.
950
Transports may raise TransportNotPossible if OS-level locks cannot be
951
taken over this transport.
855
953
:return: A lock object, which should contain an unlock() function.
857
raise NotImplementedError(self.lock_read)
955
raise errors.TransportNotPossible("transport locks not supported on %s" % self)
859
957
def lock_write(self, relpath):
860
958
"""Lock the given file for exclusive (write) access.
861
WARNING: many transports do not support this, so trying avoid using it
960
WARNING: many transports do not support this, so trying avoid using it.
961
These methods may be removed in the future.
963
Transports may raise TransportNotPossible if OS-level locks cannot be
964
taken over this transport.
863
966
:return: A lock object, which should contain an unlock() function.
865
raise NotImplementedError(self.lock_write)
968
raise errors.TransportNotPossible("transport locks not supported on %s" % self)
867
970
def is_readonly(self):
868
971
"""Return true if this connection cannot be written to."""
943
1046
for factory in factory_list:
945
1048
return factory(base), None
946
except DependencyNotPresent, e:
1049
except errors.DependencyNotPresent, e:
947
1050
mutter("failed to instantiate transport %r for %r: %r" %
948
1051
(factory, base, e))
998
1101
def adapt(self, test):
1102
result = unittest.TestSuite()
1000
1103
for klass, server_factory in self._test_permutations():
1001
1104
new_test = deepcopy(test)
1002
1105
new_test.transport_class = klass
1011
1114
def get_transport_test_permutations(self, module):
1012
1115
"""Get the permutations module wants to have tested."""
1013
if not hasattr(module, 'get_test_permutations'):
1116
if getattr(module, 'get_test_permutations', None) is None:
1014
1117
warning("transport module %s doesn't provide get_test_permutations()"
1015
1118
% module.__name__)
1078
1181
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1079
1182
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1080
1183
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1081
register_lazy_transport('vfat+',
1184
register_lazy_transport('vfat+',
1082
1185
'bzrlib.transport.fakevfat',
1083
1186
'FakeVFATTransportDecorator')
1187
register_lazy_transport('bzr://',
1188
'bzrlib.transport.smart',
1189
'SmartTCPTransport')
1190
register_lazy_transport('bzr+ssh://',
1191
'bzrlib.transport.smart',
1192
'SmartSSHTransport')