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
17
"""Transport for the local filesystem.
19
This is a fairly thin wrapper on regular file IO."""
23
from stat import ST_MODE, S_ISDIR, ST_SIZE
27
from bzrlib.trace import mutter
28
from bzrlib.transport import Transport
29
from bzrlib.osutils import abspath, realpath, normpath, pathjoin, rename
32
class LocalTransport(Transport):
33
"""This is the transport agent for local filesystem access."""
35
def __init__(self, base):
36
"""Set the base path where files will be stored."""
37
if base.startswith('file://'):
39
# realpath is incompatible with symlinks. When we traverse
40
# up we might be able to normpath stuff. RBC 20051003
41
super(LocalTransport, self).__init__(normpath(abspath(base)))
43
def should_cache(self):
46
def clone(self, offset=None):
47
"""Return a new LocalTransport with root at self.base + offset
48
Because the local filesystem does not require a connection,
49
we can just return a new object.
52
return LocalTransport(self.base)
54
return LocalTransport(self.abspath(offset))
56
def abspath(self, relpath):
57
"""Return the full url to the given relative URL.
58
This can be supplied with a string or a list
60
assert isinstance(relpath, basestring), (type(relpath), relpath)
61
return pathjoin(self.base, urllib.unquote(relpath))
63
def relpath(self, abspath):
64
"""Return the local path portion from a given absolute path.
66
from bzrlib.osutils import relpath
69
return relpath(self.base, abspath)
71
def has(self, relpath):
72
return os.access(self.abspath(relpath), os.F_OK)
74
def get(self, relpath):
75
"""Get the file at the given relative path.
77
:param relpath: The relative path to the file
80
path = self.abspath(relpath)
81
return open(path, 'rb')
82
except (IOError, OSError),e:
83
self._translate_error(e, path)
85
def put(self, relpath, f, mode=None):
86
"""Copy the file-like or string object into the location.
88
:param relpath: Location to put the contents, relative to base.
89
:param f: File-like or string object.
91
from bzrlib.atomicfile import AtomicFile
95
path = self.abspath(relpath)
96
fp = AtomicFile(path, 'wb', new_mode=mode)
97
except (IOError, OSError),e:
98
self._translate_error(e, path)
105
def iter_files_recursive(self):
106
"""Iter the relative paths of files in the transports sub-tree."""
107
queue = list(self.list_dir(u'.'))
109
relpath = urllib.quote(queue.pop(0))
110
st = self.stat(relpath)
111
if S_ISDIR(st[ST_MODE]):
112
for i, basename in enumerate(self.list_dir(relpath)):
113
queue.insert(i, relpath+'/'+basename)
117
def mkdir(self, relpath, mode=None):
118
"""Create a directory at the given path."""
121
path = self.abspath(relpath)
125
except (IOError, OSError),e:
126
self._translate_error(e, path)
128
def append(self, relpath, f):
129
"""Append the text in the file-like object into the final
132
fp = open(self.abspath(relpath), 'ab')
135
def copy(self, rel_from, rel_to):
136
"""Copy the item at rel_from to the location at rel_to"""
138
path_from = self.abspath(rel_from)
139
path_to = self.abspath(rel_to)
141
shutil.copy(path_from, path_to)
142
except (IOError, OSError),e:
143
# TODO: What about path_to?
144
self._translate_error(e, path_from)
146
def move(self, rel_from, rel_to):
147
"""Move the item at rel_from to the location at rel_to"""
148
path_from = self.abspath(rel_from)
149
path_to = self.abspath(rel_to)
152
rename(path_from, path_to)
153
except (IOError, OSError),e:
154
# TODO: What about path_to?
155
self._translate_error(e, path_from)
157
def delete(self, relpath):
158
"""Delete the item at relpath"""
161
path = self.abspath(relpath)
163
except (IOError, OSError),e:
164
# TODO: What about path_to?
165
self._translate_error(e, path)
167
def copy_to(self, relpaths, other, mode=None, pb=None):
168
"""Copy a set of entries from self into another Transport.
170
:param relpaths: A list/generator of entries to be copied.
172
if isinstance(other, LocalTransport):
173
# Both from & to are on the local filesystem
174
# Unfortunately, I can't think of anything faster than just
175
# copying them across, one by one :(
178
total = self._get_total(relpaths)
180
for path in relpaths:
181
self._update_pb(pb, 'copy-to', count, total)
183
mypath = self.abspath(path)
184
otherpath = other.abspath(path)
185
shutil.copy(mypath, otherpath)
187
os.chmod(otherpath, mode)
188
except (IOError, OSError),e:
189
self._translate_error(e, path)
193
return super(LocalTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
196
"""See Transport.listable."""
199
def list_dir(self, relpath):
200
"""Return a list of all files at the given location.
201
WARNING: many transports do not support this, so trying avoid using
202
it if at all possible.
206
path = self.abspath(relpath)
207
return os.listdir(path)
208
except (IOError, OSError),e:
209
self._translate_error(e, path)
211
def stat(self, relpath):
212
"""Return the stat information for a file.
216
path = self.abspath(relpath)
218
except (IOError, OSError),e:
219
self._translate_error(e, path)
221
def lock_read(self, relpath):
222
"""Lock the given file for shared (read) access.
223
:return: A lock object, which should be passed to Transport.unlock()
225
from bzrlib.lock import ReadLock
226
return ReadLock(self.abspath(relpath))
228
def lock_write(self, relpath):
229
"""Lock the given file for exclusive (write) access.
230
WARNING: many transports do not support this, so trying avoid using it
232
:return: A lock object, which should be passed to Transport.unlock()
234
from bzrlib.lock import WriteLock
235
return WriteLock(self.abspath(relpath))
238
class ScratchTransport(LocalTransport):
239
"""A transport that works in a temporary dir and cleans up after itself.
241
The dir only exists for the lifetime of the Python object.
242
Obviously you should not put anything precious in it.
245
def __init__(self, base=None):
247
base = tempfile.mkdtemp()
248
super(ScratchTransport, self).__init__(base)
251
shutil.rmtree(self.base, ignore_errors=True)
252
mutter("%r destroyed" % self)