1
# Copyright (C) 2005 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
"""Implementation of Transport for the local filesystem.
19
from bzrlib.transport import Transport, register_transport, \
20
TransportError, NoSuchFile, FileExists
23
class LocalTransportError(TransportError):
26
class LocalTransport(Transport):
27
"""This is the transport agent for local filesystem access."""
29
def __init__(self, base):
30
"""Set the base path where files will be stored."""
31
if base.startswith('file://'):
33
# realpath is incompatible with symlinks. When we traverse
34
# up we might be able to normpath stuff. RBC 20051003
35
super(LocalTransport, self).__init__(
36
os.path.normpath(os.path.abspath(base)))
38
def should_cache(self):
41
def clone(self, offset=None):
42
"""Return a new LocalTransport with root at self.base + offset
43
Because the local filesystem does not require a connection,
44
we can just return a new object.
47
return LocalTransport(self.base)
49
return LocalTransport(self.abspath(offset))
51
def abspath(self, relpath):
52
"""Return the full url to the given relative path.
53
This can be supplied with a string or a list
55
if isinstance(relpath, basestring):
57
return os.path.join(self.base, *relpath)
59
def relpath(self, abspath):
60
"""Return the local path portion from a given absolute path.
62
from bzrlib.branch import _relpath
63
return _relpath(self.base, abspath)
65
def has(self, relpath):
66
return os.access(self.abspath(relpath), os.F_OK)
68
def get(self, relpath):
69
"""Get the file at the given relative path.
71
:param relpath: The relative path to the file
74
path = self.abspath(relpath)
75
return open(path, 'rb')
77
if e.errno in (errno.ENOENT, errno.ENOTDIR):
78
raise NoSuchFile('File or directory %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)
103
raise LocalTransportError(orig_error=e)
105
def put(self, relpath, f):
106
"""Copy the file-like or string object into the location.
108
:param relpath: Location to put the contents, relative to base.
109
:param f: File-like or string object.
111
from bzrlib.atomicfile import AtomicFile
114
path = self.abspath(relpath)
115
fp = AtomicFile(path, 'wb')
117
if e.errno == errno.ENOENT:
118
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
119
raise LocalTransportError(orig_error=e)
126
def mkdir(self, relpath):
127
"""Create a directory at the given path."""
129
os.mkdir(self.abspath(relpath))
131
if e.errno == errno.EEXIST:
132
raise FileExists(orig_error=e)
133
elif e.errno == errno.ENOENT:
134
raise NoSuchFile(orig_error=e)
135
raise LocalTransportError(orig_error=e)
137
def append(self, relpath, f):
138
"""Append the text in the file-like object into the final
141
fp = open(self.abspath(relpath), 'ab')
144
def copy(self, rel_from, rel_to):
145
"""Copy the item at rel_from to the location at rel_to"""
147
path_from = self.abspath(rel_from)
148
path_to = self.abspath(rel_to)
150
shutil.copy(path_from, path_to)
152
raise LocalTransportError(orig_error=e)
154
def move(self, rel_from, rel_to):
155
"""Move the item at rel_from to the location at rel_to"""
156
path_from = self.abspath(rel_from)
157
path_to = self.abspath(rel_to)
160
os.rename(path_from, path_to)
162
raise LocalTransportError(orig_error=e)
164
def delete(self, relpath):
165
"""Delete the item at relpath"""
167
os.remove(self.abspath(relpath))
169
raise LocalTransportError(orig_error=e)
171
def copy_to(self, relpaths, other, pb=None):
172
"""Copy a set of entries from self into another Transport.
174
:param relpaths: A list/generator of entries to be copied.
176
if isinstance(other, LocalTransport):
177
# Both from & to are on the local filesystem
178
# Unfortunately, I can't think of anything faster than just
179
# copying them across, one by one :(
182
total = self._get_total(relpaths)
184
for path in relpaths:
185
self._update_pb(pb, 'copy-to', count, total)
186
shutil.copy(self.abspath(path), other.abspath(path))
190
return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
193
"""See Transport.listable."""
196
def list_dir(self, relpath):
197
"""Return a list of all files at the given location.
198
WARNING: many transports do not support this, so trying avoid using
199
it if at all possible.
202
return os.listdir(self.abspath(relpath))
204
raise LocalTransportError(orig_error=e)
206
def stat(self, relpath):
207
"""Return the stat information for a file.
210
return os.stat(self.abspath(relpath))
212
raise LocalTransportError(orig_error=e)
214
def lock_read(self, relpath):
215
"""Lock the given file for shared (read) access.
216
:return: A lock object, which should be passed to Transport.unlock()
218
from bzrlib.lock import ReadLock
219
return ReadLock(self.abspath(relpath))
221
def lock_write(self, relpath):
222
"""Lock the given file for exclusive (write) access.
223
WARNING: many transports do not support this, so trying avoid using it
225
:return: A lock object, which should be passed to Transport.unlock()
227
from bzrlib.lock import WriteLock
228
return WriteLock(self.abspath(relpath))
230
# If nothing else matches, try the LocalTransport
231
register_transport(None, LocalTransport)
232
register_transport('file://', LocalTransport)