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."""
24
from stat import ST_MODE, S_ISDIR, ST_SIZE
28
from bzrlib.trace import mutter
3
An implementation of the Transport object for local
29
7
from bzrlib.transport import Transport, register_transport, \
30
8
TransportError, NoSuchFile, FileExists
31
from bzrlib.osutils import abspath
33
11
class LocalTransportError(TransportError):
37
14
class LocalTransport(Transport):
38
15
"""This is the transport agent for local filesystem access."""
41
18
"""Set the base path where files will be stored."""
42
19
if base.startswith('file://'):
44
# realpath is incompatible with symlinks. When we traverse
45
# up we might be able to normpath stuff. RBC 20051003
46
super(LocalTransport, self).__init__(
47
os.path.normpath(abspath(base)))
21
super(LocalTransport, self).__init__(os.path.realpath(base))
49
23
def should_cache(self):
60
34
return LocalTransport(self.abspath(offset))
62
36
def abspath(self, relpath):
63
"""Return the full url to the given relative URL.
37
"""Return the full url to the given relative path.
64
38
This can be supplied with a string or a list
66
assert isinstance(relpath, basestring), (type(relpath), relpath)
67
return os.path.join(self.base, urllib.unquote(relpath))
40
if isinstance(relpath, basestring):
42
return os.path.join(self.base, *relpath)
69
44
def relpath(self, abspath):
70
45
"""Return the local path portion from a given absolute path.
72
from bzrlib.osutils import relpath
75
return relpath(self.base, abspath)
47
from bzrlib.branch import _relpath
48
return _relpath(self.base, abspath)
77
50
def has(self, relpath):
78
51
return os.access(self.abspath(relpath), os.F_OK)
86
59
path = self.abspath(relpath)
87
60
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)
62
if e.errno == errno.ENOENT:
63
raise NoSuchFile('File %r does not exist' % path, orig_error=e)
91
64
raise LocalTransportError(orig_error=e)
93
66
def put(self, relpath, f):
114
def iter_files_recursive(self):
115
"""Iter the relative paths of files in the transports sub-tree."""
116
queue = list(self.list_dir(u'.'))
118
relpath = urllib.quote(queue.pop(0))
119
st = self.stat(relpath)
120
if S_ISDIR(st[ST_MODE]):
121
for i, basename in enumerate(self.list_dir(relpath)):
122
queue.insert(i, relpath+'/'+basename)
126
87
def mkdir(self, relpath):
127
88
"""Create a directory at the given path."""
184
145
for path in relpaths:
185
146
self._update_pb(pb, 'copy-to', count, total)
187
shutil.copy(self.abspath(path), other.abspath(path))
189
if e.errno in (errno.ENOENT, errno.ENOTDIR):
190
raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
191
raise LocalTransportError(orig_error=e)
147
shutil.copy(self.abspath(path), other.abspath(path))
195
151
return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
198
"""See Transport.listable."""
154
def async_get(self, relpath):
155
"""Make a request for an file at the given location, but
156
don't worry about actually getting it yet.
160
raise NotImplementedError
201
162
def list_dir(self, relpath):
202
163
"""Return a list of all files at the given location.
232
193
from bzrlib.lock import WriteLock
233
194
return WriteLock(self.abspath(relpath))
236
class ScratchTransport(LocalTransport):
237
"""A transport that works in a temporary dir and cleans up after itself.
239
The dir only exists for the lifetime of the Python object.
240
Obviously you should not put anything precious in it.
243
def __init__(self, base=None):
245
base = tempfile.mkdtemp()
246
super(ScratchTransport, self).__init__(base)
249
shutil.rmtree(self.base, ignore_errors=True)
250
mutter("%r destroyed" % self)
196
# If nothing else matches, try the LocalTransport
197
register_transport(None, LocalTransport)
198
register_transport('file://', LocalTransport)