13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
"""Implementation of Transport for the local filesystem.
17
"""Transport for the local filesystem.
19
This is a fairly thin wrapper on regular file IO."""
24
from stat import ST_MODE, S_ISDIR, ST_SIZE
28
from bzrlib.trace import mutter
19
29
from bzrlib.transport import Transport, register_transport, \
20
30
TransportError, NoSuchFile, FileExists
31
from bzrlib.osutils import abspath
23
33
class LocalTransportError(TransportError):
26
37
class LocalTransport(Transport):
27
38
"""This is the transport agent for local filesystem access."""
33
44
# realpath is incompatible with symlinks. When we traverse
34
45
# up we might be able to normpath stuff. RBC 20051003
35
46
super(LocalTransport, self).__init__(
36
os.path.normpath(os.path.abspath(base)))
47
os.path.normpath(abspath(base)))
38
49
def should_cache(self):
49
60
return LocalTransport(self.abspath(offset))
51
62
def abspath(self, relpath):
52
"""Return the full url to the given relative path.
63
"""Return the full url to the given relative URL.
53
64
This can be supplied with a string or a list
55
if isinstance(relpath, basestring):
57
return os.path.join(self.base, *relpath)
66
assert isinstance(relpath, basestring), (type(relpath), relpath)
67
return os.path.join(self.base, urllib.unquote(relpath))
59
69
def relpath(self, abspath):
60
70
"""Return the local path portion from a given absolute path.
62
from bzrlib.branch import _relpath
63
return _relpath(self.base, abspath)
72
from bzrlib.osutils import relpath
75
return relpath(self.base, abspath)
65
77
def has(self, relpath):
66
78
return os.access(self.abspath(relpath), os.F_OK)
74
86
path = self.abspath(relpath)
75
87
return open(path, 'rb')
77
if e.errno == errno.ENOENT:
78
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
79
raise LocalTransportError(orig_error=e)
81
def get_partial(self, relpath, start, length=None):
82
"""Get just part of a file.
84
:param relpath: Path to the file, relative to base
85
:param start: The starting position to read from
86
:param length: The length to read. A length of None indicates
87
read to the end of the file.
88
:return: A file-like object containing at least the specified bytes.
89
Some implementations may return objects which can be read
90
past this length, but this is not guaranteed.
92
# LocalTransport.get_partial() doesn't care about the length
93
# argument, because it is using a local file, and thus just
94
# returns the file seek'ed to the appropriate location.
96
path = self.abspath(relpath)
101
if e.errno == errno.ENOENT:
102
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
89
if e.errno in (errno.ENOENT, errno.ENOTDIR):
90
raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
103
91
raise LocalTransportError(orig_error=e)
105
93
def put(self, relpath, f):
114
def iter_files_recursive(self):
115
"""Iter the relative paths of files in the transports sub-tree."""
116
queue = list(self.list_dir('.'))
118
relpath = urllib.quote(queue.pop(0))
119
st = self.stat(relpath)
120
if S_ISDIR(st[ST_MODE]):
121
for i, basename in enumerate(self.list_dir(relpath)):
122
queue.insert(i, relpath+'/'+basename)
126
126
def mkdir(self, relpath):
127
127
"""Create a directory at the given path."""
184
184
for path in relpaths:
185
185
self._update_pb(pb, 'copy-to', count, total)
186
shutil.copy(self.abspath(path), other.abspath(path))
187
shutil.copy(self.abspath(path), other.abspath(path))
189
if e.errno in (errno.ENOENT, errno.ENOTDIR):
190
raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
191
raise LocalTransportError(orig_error=e)
190
195
return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
198
"""See Transport.listable."""
193
201
def list_dir(self, relpath):
194
202
"""Return a list of all files at the given location.
224
232
from bzrlib.lock import WriteLock
225
233
return WriteLock(self.abspath(relpath))
227
# If nothing else matches, try the LocalTransport
228
register_transport(None, LocalTransport)
229
register_transport('file://', LocalTransport)
236
class ScratchTransport(LocalTransport):
237
"""A transport that works in a temporary dir and cleans up after itself.
239
The dir only exists for the lifetime of the Python object.
240
Obviously you should not put anything precious in it.
243
def __init__(self, base=None):
245
base = tempfile.mkdtemp()
246
super(ScratchTransport, self).__init__(base)
249
shutil.rmtree(self.base, ignore_errors=True)
250
mutter("%r destroyed" % self)