19
19
This is a fairly thin wrapper on regular file IO."""
24
23
from stat import ST_MODE, S_ISDIR, ST_SIZE
28
27
from bzrlib.trace import mutter
29
from bzrlib.transport import Transport, register_transport, \
30
TransportError, NoSuchFile, FileExists
31
from bzrlib.osutils import abspath
33
class LocalTransportError(TransportError):
28
from bzrlib.transport import Transport, Server
29
from bzrlib.osutils import abspath, realpath, normpath, pathjoin, rename
37
32
class LocalTransport(Transport):
44
39
# realpath is incompatible with symlinks. When we traverse
45
40
# up we might be able to normpath stuff. RBC 20051003
46
super(LocalTransport, self).__init__(
47
os.path.normpath(abspath(base)))
41
base = normpath(abspath(base))
44
super(LocalTransport, self).__init__(base)
49
46
def should_cache(self):
63
60
"""Return the full url to the given relative URL.
64
61
This can be supplied with a string or a list
66
assert isinstance(relpath, basestring)
67
return os.path.join(self.base, urllib.unquote(relpath))
63
assert isinstance(relpath, basestring), (type(relpath), relpath)
64
return pathjoin(self.base, urllib.unquote(relpath))
69
66
def relpath(self, abspath):
70
67
"""Return the local path portion from a given absolute path.
72
69
from bzrlib.osutils import relpath
73
70
if abspath is None:
75
return relpath(self.base, abspath)
72
if abspath.endswith('/'):
73
abspath = abspath[:-1]
74
return relpath(self.base[:-1], abspath)
77
76
def has(self, relpath):
78
77
return os.access(self.abspath(relpath), os.F_OK)
86
85
path = self.abspath(relpath)
87
86
return open(path, 'rb')
89
if e.errno in (errno.ENOENT, errno.ENOTDIR):
90
raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
91
raise LocalTransportError(orig_error=e)
87
except (IOError, OSError),e:
88
self._translate_error(e, path)
93
def put(self, relpath, f):
90
def put(self, relpath, f, mode=None):
94
91
"""Copy the file-like or string object into the location.
96
93
:param relpath: Location to put the contents, relative to base.
99
96
from bzrlib.atomicfile import AtomicFile
102
100
path = self.abspath(relpath)
103
fp = AtomicFile(path, 'wb')
105
if e.errno == errno.ENOENT:
106
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
107
raise LocalTransportError(orig_error=e)
101
fp = AtomicFile(path, 'wb', new_mode=mode)
102
except (IOError, OSError),e:
103
self._translate_error(e, path)
109
105
self._pump(f, fp)
114
110
def iter_files_recursive(self):
115
111
"""Iter the relative paths of files in the transports sub-tree."""
116
queue = list(self.list_dir('.'))
112
queue = list(self.list_dir(u'.'))
118
relpath = queue.pop(0)
114
relpath = urllib.quote(queue.pop(0))
119
115
st = self.stat(relpath)
120
116
if S_ISDIR(st[ST_MODE]):
121
117
for i, basename in enumerate(self.list_dir(relpath)):
126
def mkdir(self, relpath):
122
def mkdir(self, relpath, mode=None):
127
123
"""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)
126
path = self.abspath(relpath)
130
except (IOError, OSError),e:
131
self._translate_error(e, path)
137
133
def append(self, relpath, f):
138
134
"""Append the text in the file-like object into the final
141
fp = open(self.abspath(relpath), 'ab')
138
fp = open(self.abspath(relpath), 'ab')
139
except (IOError, OSError),e:
140
self._translate_error(e, relpath)
142
141
self._pump(f, fp)
144
143
def copy(self, rel_from, rel_to):
148
147
path_to = self.abspath(rel_to)
150
149
shutil.copy(path_from, path_to)
152
raise LocalTransportError(orig_error=e)
150
except (IOError, OSError),e:
151
# TODO: What about path_to?
152
self._translate_error(e, path_from)
154
154
def move(self, rel_from, rel_to):
155
155
"""Move the item at rel_from to the location at rel_to"""
157
157
path_to = self.abspath(rel_to)
160
os.rename(path_from, path_to)
162
raise LocalTransportError(orig_error=e)
160
rename(path_from, path_to)
161
except (IOError, OSError),e:
162
# TODO: What about path_to?
163
self._translate_error(e, path_from)
164
165
def delete(self, relpath):
165
166
"""Delete the item at relpath"""
167
os.remove(self.abspath(relpath))
169
raise LocalTransportError(orig_error=e)
169
path = self.abspath(relpath)
171
except (IOError, OSError),e:
172
# TODO: What about path_to?
173
self._translate_error(e, path)
171
def copy_to(self, relpaths, other, pb=None):
175
def copy_to(self, relpaths, other, mode=None, pb=None):
172
176
"""Copy a set of entries from self into another Transport.
174
178
:param relpaths: A list/generator of entries to be copied.
184
188
for path in relpaths:
185
189
self._update_pb(pb, 'copy-to', count, total)
186
shutil.copy(self.abspath(path), other.abspath(path))
191
mypath = self.abspath(path)
192
otherpath = other.abspath(path)
193
shutil.copy(mypath, otherpath)
195
os.chmod(otherpath, mode)
196
except (IOError, OSError),e:
197
self._translate_error(e, path)
190
return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
201
return super(LocalTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
192
203
def listable(self):
193
204
"""See Transport.listable."""
198
209
WARNING: many transports do not support this, so trying avoid using
199
210
it if at all possible.
202
return os.listdir(self.abspath(relpath))
204
raise LocalTransportError(orig_error=e)
214
path = self.abspath(relpath)
215
return os.listdir(path)
216
except (IOError, OSError),e:
217
self._translate_error(e, path)
206
219
def stat(self, relpath):
207
220
"""Return the stat information for a file.
210
return os.stat(self.abspath(relpath))
212
raise LocalTransportError(orig_error=e)
224
path = self.abspath(relpath)
226
except (IOError, OSError),e:
227
self._translate_error(e, path)
214
229
def lock_read(self, relpath):
215
230
"""Lock the given file for shared (read) access.
216
231
:return: A lock object, which should be passed to Transport.unlock()
218
233
from bzrlib.lock import ReadLock
219
return ReadLock(self.abspath(relpath))
236
path = self.abspath(relpath)
237
return ReadLock(path)
238
except (IOError, OSError), e:
239
self._translate_error(e, path)
221
241
def lock_write(self, relpath):
222
242
"""Lock the given file for exclusive (write) access.
227
247
from bzrlib.lock import WriteLock
228
248
return WriteLock(self.abspath(relpath))
250
def rmdir(self, relpath):
251
"""See Transport.rmdir."""
254
path = self.abspath(relpath)
256
except (IOError, OSError),e:
257
self._translate_error(e, path)
231
259
class ScratchTransport(LocalTransport):
232
260
"""A transport that works in a temporary dir and cleans up after itself.
243
271
def __del__(self):
244
272
shutil.rmtree(self.base, ignore_errors=True)
245
273
mutter("%r destroyed" % self)
276
class LocalRelpathServer(Server):
277
"""A pretend server for local transports, using relpaths."""
280
"""See Transport.Server.get_url."""
284
class LocalAbspathServer(Server):
285
"""A pretend server for local transports, using absolute paths."""
288
"""See Transport.Server.get_url."""
289
return os.path.abspath("")
292
class LocalURLServer(Server):
293
"""A pretend server for local transports, using file:// urls."""
296
"""See Transport.Server.get_url."""
297
# FIXME: \ to / on windows
298
return "file://%s" % os.path.abspath("")
301
def get_test_permutations():
302
"""Return the permutations to be used in testing."""
303
return [(LocalTransport, LocalRelpathServer),
304
(LocalTransport, LocalAbspathServer),
305
(LocalTransport, LocalURLServer),