169
169
except ValueError:
170
170
# TODO: Should this be ConnectionError?
171
raise errors.TransportError('%s: invalid port number' % port)
171
raise errors.TransportError(
172
'invalid port number %s in url:\n%s' % (port, url))
172
173
host = urllib.unquote(host)
174
175
path = urllib.unquote(path)
201
202
implementations can do pipelining.
202
203
In general implementations should support having a generator or a list
203
204
as an argument (ie always iterate, never index)
206
:ivar base: Base URL for the transport; should always end in a slash.
206
209
# implementations can override this if it is more efficient
308
311
def abspath(self, relpath):
309
312
"""Return the full url to the given relative path.
310
This can be supplied with a string or a list
312
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
315
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)
317
366
def relpath(self, abspath):
318
367
"""Return the local path portion from a given absolute path.
345
394
Note that some transports MAY allow querying on directories, but this
346
395
is not part of the protocol. In other words, the results of
347
396
t.has("a_directory_name") are undefined.
349
400
raise NotImplementedError(self.has)
392
444
return self.get(relpath).read()
446
def get_smart_client(self):
447
"""Return a smart client for this transport if possible.
449
:raises NoSmartServer: if no smart server client is available.
451
raise errors.NoSmartServer(self.base)
394
453
def readv(self, relpath, offsets):
395
454
"""Get parts of the file at the given relative path.
403
462
fp = self.get(relpath)
404
return self._seek_and_read(fp, offsets)
463
return self._seek_and_read(fp, offsets, relpath)
406
def _seek_and_read(self, fp, offsets):
465
def _seek_and_read(self, fp, offsets, relpath='<unknown>'):
407
466
"""An implementation of readv that uses fp.seek and fp.read.
409
468
This uses _coalesce_offsets to issue larger reads and fewer seeks.
432
491
fp.seek(c_offset.start)
433
492
data = fp.read(c_offset.length)
493
if len(data) < c_offset.length:
494
raise errors.ShortReadvError(relpath, c_offset.start,
495
c_offset.length, actual=len(data))
434
496
for suboffset, subsize in c_offset.ranges:
435
497
key = (c_offset.start+suboffset, subsize)
436
498
data_map[key] = data[suboffset:suboffset+subsize]
637
699
return self.append_file(relpath, f, mode=mode)
639
701
def append_file(self, relpath, f, mode=None):
640
"""Append the text in the file-like object to the supplied location.
642
returns the length of relpath before the content was written to it.
644
If the file does not exist, it is created with the supplied mode.
702
"""Append bytes from a file-like object to a file at relpath.
704
The file is created if it does not already exist.
706
:param f: a file-like object of the bytes to append.
707
:param mode: Unix mode for newly created files. This is not used for
710
:returns: the length of relpath before the content was written to it.
646
712
symbol_versioning.warn('Transport %s should implement append_file,'
647
713
' rather than implementing append() as of'
651
717
return self.append(relpath, f, mode=mode)
653
719
def append_bytes(self, relpath, bytes, mode=None):
654
"""Append the text in the string object to the supplied location.
656
returns the length of relpath before the content was written to it.
658
If the file does not exist, it is created with the supplied mode.
720
"""Append bytes to a file at relpath.
722
The file is created if it does not already exist.
725
:param f: a string of the bytes to append.
726
:param mode: Unix mode for newly created files. This is not used for
729
:returns: the length of relpath before the content was written to it.
660
731
assert isinstance(bytes, str), \
661
732
'bytes must be a plain string, not %s' % type(bytes)
851
922
WARNING: many transports do not support this, so trying avoid using
852
923
it if at all possible.
854
raise errors.TransportNotPossible("This transport has not "
925
raise errors.TransportNotPossible("Transport %r has not "
855
926
"implemented list_dir "
856
927
"(but must claim to be listable "
857
"to trigger this error).")
928
"to trigger this error)."
859
931
def lock_read(self, relpath):
860
932
"""Lock the given file for shared (read) access.
861
WARNING: many transports do not support this, so trying avoid using it
934
WARNING: many transports do not support this, so trying avoid using it.
935
These methods may be removed in the future.
937
Transports may raise TransportNotPossible if OS-level locks cannot be
938
taken over this transport.
863
940
:return: A lock object, which should contain an unlock() function.
865
raise NotImplementedError(self.lock_read)
942
raise errors.TransportNotPossible("transport locks not supported on %s" % self)
867
944
def lock_write(self, relpath):
868
945
"""Lock the given file for exclusive (write) access.
869
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.
871
953
:return: A lock object, which should contain an unlock() function.
873
raise NotImplementedError(self.lock_write)
955
raise errors.TransportNotPossible("transport locks not supported on %s" % self)
875
957
def is_readonly(self):
876
958
"""Return true if this connection cannot be written to."""
1086
1168
register_lazy_transport('memory://', 'bzrlib.transport.memory', 'MemoryTransport')
1087
1169
register_lazy_transport('readonly+', 'bzrlib.transport.readonly', 'ReadonlyTransportDecorator')
1088
1170
register_lazy_transport('fakenfs+', 'bzrlib.transport.fakenfs', 'FakeNFSTransportDecorator')
1089
register_lazy_transport('vfat+',
1171
register_lazy_transport('vfat+',
1090
1172
'bzrlib.transport.fakevfat',
1091
1173
'FakeVFATTransportDecorator')
1174
register_lazy_transport('bzr://',
1175
'bzrlib.transport.smart',
1176
'SmartTCPTransport')
1177
register_lazy_transport('bzr+ssh://',
1178
'bzrlib.transport.smart',
1179
'SmartSSHTransport')