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
super(LocalTransport, self).__init__(os.path.realpath(base))
35
def should_cache(self):
38
def clone(self, offset=None):
39
"""Return a new LocalTransport with root at self.base + offset
40
Because the local filesystem does not require a connection,
41
we can just return a new object.
44
return LocalTransport(self.base)
46
return LocalTransport(self.abspath(offset))
48
def abspath(self, relpath):
49
"""Return the full url to the given relative path.
50
This can be supplied with a string or a list
52
if isinstance(relpath, basestring):
54
return os.path.join(self.base, *relpath)
56
def relpath(self, abspath):
57
"""Return the local path portion from a given absolute path.
59
from bzrlib.branch import _relpath
60
return _relpath(self.base, abspath)
62
def has(self, relpath):
63
return os.access(self.abspath(relpath), os.F_OK)
65
def get(self, relpath):
66
"""Get the file at the given relative path.
68
:param relpath: The relative path to the file
71
path = self.abspath(relpath)
72
return open(path, 'rb')
74
if e.errno == errno.ENOENT:
75
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
76
raise LocalTransportError(orig_error=e)
78
def get_partial(self, relpath, start, length=None):
79
"""Get just part of a file.
81
:param relpath: Path to the file, relative to base
82
:param start: The starting position to read from
83
:param length: The length to read. A length of None indicates
84
read to the end of the file.
85
:return: A file-like object containing at least the specified bytes.
86
Some implementations may return objects which can be read
87
past this length, but this is not guaranteed.
89
# LocalTransport.get_partial() doesn't care about the length
90
# argument, because it is using a local file, and thus just
91
# returns the file seek'ed to the appropriate location.
93
path = self.abspath(relpath)
98
if e.errno == errno.ENOENT:
99
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
100
raise LocalTransportError(orig_error=e)
102
def put(self, relpath, f):
103
"""Copy the file-like or string object into the location.
105
:param relpath: Location to put the contents, relative to base.
106
:param f: File-like or string object.
108
from bzrlib.atomicfile import AtomicFile
111
path = self.abspath(relpath)
112
fp = AtomicFile(path, 'wb')
114
if e.errno == errno.ENOENT:
115
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
116
raise LocalTransportError(orig_error=e)
123
def mkdir(self, relpath):
124
"""Create a directory at the given path."""
126
os.mkdir(self.abspath(relpath))
128
if e.errno == errno.EEXIST:
129
raise FileExists(orig_error=e)
130
elif e.errno == errno.ENOENT:
131
raise NoSuchFile(orig_error=e)
132
raise LocalTransportError(orig_error=e)
134
def append(self, relpath, f):
135
"""Append the text in the file-like object into the final
138
fp = open(self.abspath(relpath), 'ab')
141
def copy(self, rel_from, rel_to):
142
"""Copy the item at rel_from to the location at rel_to"""
144
path_from = self.abspath(rel_from)
145
path_to = self.abspath(rel_to)
147
shutil.copy(path_from, path_to)
149
raise LocalTransportError(orig_error=e)
151
def move(self, rel_from, rel_to):
152
"""Move the item at rel_from to the location at rel_to"""
153
path_from = self.abspath(rel_from)
154
path_to = self.abspath(rel_to)
157
os.rename(path_from, path_to)
159
raise LocalTransportError(orig_error=e)
161
def delete(self, relpath):
162
"""Delete the item at relpath"""
164
os.remove(self.abspath(relpath))
166
raise LocalTransportError(orig_error=e)
168
def copy_to(self, relpaths, other, pb=None):
169
"""Copy a set of entries from self into another Transport.
171
:param relpaths: A list/generator of entries to be copied.
173
if isinstance(other, LocalTransport):
174
# Both from & to are on the local filesystem
175
# Unfortunately, I can't think of anything faster than just
176
# copying them across, one by one :(
179
total = self._get_total(relpaths)
181
for path in relpaths:
182
self._update_pb(pb, 'copy-to', count, total)
183
shutil.copy(self.abspath(path), other.abspath(path))
187
return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
190
def list_dir(self, relpath):
191
"""Return a list of all files at the given location.
192
WARNING: many transports do not support this, so trying avoid using
193
it if at all possible.
196
return os.listdir(self.abspath(relpath))
198
raise LocalTransportError(orig_error=e)
200
def stat(self, relpath):
201
"""Return the stat information for a file.
204
return os.stat(self.abspath(relpath))
206
raise LocalTransportError(orig_error=e)
208
def lock_read(self, relpath):
209
"""Lock the given file for shared (read) access.
210
:return: A lock object, which should be passed to Transport.unlock()
212
from bzrlib.lock import ReadLock
213
return ReadLock(self.abspath(relpath))
215
def lock_write(self, relpath):
216
"""Lock the given file for exclusive (write) access.
217
WARNING: many transports do not support this, so trying avoid using it
219
:return: A lock object, which should be passed to Transport.unlock()
221
from bzrlib.lock import WriteLock
222
return WriteLock(self.abspath(relpath))
224
# If nothing else matches, try the LocalTransport
225
register_transport(None, LocalTransport)
226
register_transport('file://', LocalTransport)