17
17
"""Transport for the local filesystem.
19
This is a fairly thin wrapper on regular file IO."""
19
This is a fairly thin wrapper on regular file IO.
24
25
from stat import ST_MODE, S_ISDIR, ST_SIZE
28
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename,
29
check_legal_path, rmtree)
30
from bzrlib.symbol_versioning import warn
28
31
from bzrlib.trace import mutter
29
32
from bzrlib.transport import Transport, Server
30
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename,
31
check_legal_path, rmtree)
33
import bzrlib.urlutils as urlutils
34
36
class LocalTransport(Transport):
37
39
def __init__(self, base):
38
40
"""Set the base path where files will be stored."""
39
if base.startswith('file://'):
40
base = base[len('file://'):]
41
# realpath is incompatible with symlinks. When we traverse
42
# up we might be able to normpath stuff. RBC 20051003
43
base = normpath(abspath(base))
41
if not base.startswith('file://'):
42
warn("Instantiating LocalTransport with a filesystem path"
43
" is deprecated as of bzr 0.8."
44
" Please use bzrlib.transport.get_transport()"
45
" or pass in a file:// url.",
49
base = urlutils.local_path_to_url(base)
44
50
if base[-1] != '/':
46
52
super(LocalTransport, self).__init__(base)
53
self._local_base = urlutils.local_path_from_url(base)
54
## mutter("_local_base: %r => %r", base, self._local_base)
48
56
def should_cache(self):
59
67
return LocalTransport(self.abspath(offset))
69
def _abspath(self, relative_reference):
70
"""Return a path for use in os calls.
72
Several assumptions are made:
73
- relative_reference does not contain '..'
74
- relative_reference is url escaped.
76
if relative_reference in ('.', ''):
77
return self._local_base
78
return self._local_base + urlutils.unescape(relative_reference)
61
80
def abspath(self, relpath):
62
81
"""Return the full url to the given relative URL."""
82
# TODO: url escape the result. RBC 20060523.
63
83
assert isinstance(relpath, basestring), (type(relpath), relpath)
64
result = normpath(pathjoin(self.base, urllib.unquote(relpath)))
65
#if result[-1] != '/':
84
# jam 20060426 Using normpath on the real path, because that ensures
85
# proper handling of stuff like
86
path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
87
return urlutils.local_path_to_url(path)
89
def local_abspath(self, relpath):
90
"""Transform the given relative path URL into the actual path on disk
92
This function only exists for the LocalTransport, since it is
93
the only one that has direct local access.
94
This is mostly for stuff like WorkingTree which needs to know
95
the local working directory.
97
This function is quite expensive: it calls realpath which resolves
100
absurl = self.abspath(relpath)
101
# mutter(u'relpath %s => base: %s, absurl %s', relpath, self.base, absurl)
102
return urlutils.local_path_from_url(absurl)
69
104
def relpath(self, abspath):
70
105
"""Return the local path portion from a given absolute path.
72
from bzrlib.osutils import relpath, strip_trailing_slash
73
107
if abspath is None:
76
return relpath(strip_trailing_slash(self.base),
77
strip_trailing_slash(abspath))
110
return urlutils.file_relpath(
111
urlutils.strip_trailing_slash(self.base),
112
urlutils.strip_trailing_slash(abspath))
79
114
def has(self, relpath):
80
return os.access(self.abspath(relpath), os.F_OK)
115
return os.access(self._abspath(relpath), os.F_OK)
82
117
def get(self, relpath):
83
118
"""Get the file at the given relative path.
135
170
self._translate_error(e, path)
137
172
def append(self, relpath, f, mode=None):
138
"""Append the text in the file-like object into the final
173
"""Append the text in the file-like object into the final location."""
174
abspath = self._abspath(relpath)
142
fp = open(self.abspath(relpath), 'ab')
144
os.chmod(self.abspath(relpath), mode)
145
except (IOError, OSError),e:
146
self._translate_error(e, relpath)
147
# win32 workaround (tell on an unwritten file returns 0)
178
fp = open(abspath, 'ab')
179
# FIXME should we really be chmodding every time ? RBC 20060523
181
os.chmod(abspath, mode)
182
except (IOError, OSError),e:
183
self._translate_error(e, relpath)
184
# win32 workaround (tell on an unwritten file returns 0)
153
193
def copy(self, rel_from, rel_to):
154
194
"""Copy the item at rel_from to the location at rel_to"""
155
path_from = self.abspath(rel_from)
156
path_to = self.abspath(rel_to)
195
path_from = self._abspath(rel_from)
196
path_to = self._abspath(rel_to)
158
198
shutil.copy(path_from, path_to)
159
199
except (IOError, OSError),e:
161
201
self._translate_error(e, path_from)
163
203
def rename(self, rel_from, rel_to):
164
path_from = self.abspath(rel_from)
204
path_from = self._abspath(rel_from)
166
206
# *don't* call bzrlib.osutils.rename, because we want to
167
207
# detect errors on rename
168
os.rename(path_from, self.abspath(rel_to))
208
os.rename(path_from, self._abspath(rel_to))
169
209
except (IOError, OSError),e:
170
210
# TODO: What about path_to?
171
211
self._translate_error(e, path_from)
173
213
def move(self, rel_from, rel_to):
174
214
"""Move the item at rel_from to the location at rel_to"""
175
path_from = self.abspath(rel_from)
176
path_to = self.abspath(rel_to)
215
path_from = self._abspath(rel_from)
216
path_to = self._abspath(rel_to)
179
219
# this version will delete the destination if necessary
186
226
"""Delete the item at relpath"""
189
path = self.abspath(relpath)
229
path = self._abspath(relpath)
191
231
except (IOError, OSError),e:
192
# TODO: What about path_to?
193
232
self._translate_error(e, path)
195
234
def copy_to(self, relpaths, other, mode=None, pb=None):
206
245
for path in relpaths:
207
246
self._update_pb(pb, 'copy-to', count, total)
209
mypath = self.abspath(path)
210
otherpath = other.abspath(path)
248
mypath = self._abspath(path)
249
otherpath = other._abspath(path)
211
250
shutil.copy(mypath, otherpath)
212
251
if mode is not None:
213
252
os.chmod(otherpath, mode)
227
266
WARNING: many transports do not support this, so trying avoid using
228
267
it if at all possible.
230
path = self.abspath(relpath)
269
path = self._abspath(relpath)
232
return [urllib.quote(entry) for entry in os.listdir(path)]
271
return [urlutils.escape(entry) for entry in os.listdir(path)]
233
272
except (IOError, OSError), e:
234
273
self._translate_error(e, path)
262
301
:return: A lock object, which should be passed to Transport.unlock()
264
303
from bzrlib.lock import WriteLock
265
return WriteLock(self.abspath(relpath))
304
return WriteLock(self._abspath(relpath))
267
306
def rmdir(self, relpath):
268
307
"""See Transport.rmdir."""
271
path = self.abspath(relpath)
310
path = self._abspath(relpath)
273
312
except (IOError, OSError),e:
274
313
self._translate_error(e, path)
284
class ScratchTransport(LocalTransport):
285
"""A transport that works in a temporary dir and cleans up after itself.
287
The dir only exists for the lifetime of the Python object.
288
Obviously you should not put anything precious in it.
291
def __init__(self, base=None):
293
base = tempfile.mkdtemp()
294
super(ScratchTransport, self).__init__(base)
297
rmtree(self.base, ignore_errors=True)
298
mutter("%r destroyed" % self)
301
323
class LocalRelpathServer(Server):
302
324
"""A pretend server for local transports, using relpaths."""