~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Martin Pool
  • Date: 2005-07-07 10:31:36 UTC
  • Revision ID: mbp@sourcefrog.net-20050707103135-9b4d911d8df6e880
- fix pwk help

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
2
 
 
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.
7
 
 
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.
12
 
 
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.
17
 
"""
18
 
 
19
 
import os
20
 
import errno
21
 
import shutil
22
 
from stat import ST_MODE, S_ISDIR, ST_SIZE
23
 
import tempfile
24
 
 
25
 
from bzrlib.trace import mutter
26
 
from bzrlib.transport import Transport, register_transport, \
27
 
    TransportError, NoSuchFile, FileExists
28
 
 
29
 
 
30
 
class LocalTransportError(TransportError):
31
 
    pass
32
 
 
33
 
 
34
 
class LocalTransport(Transport):
35
 
    """This is the transport agent for local filesystem access."""
36
 
 
37
 
    def __init__(self, base):
38
 
        """Set the base path where files will be stored."""
39
 
        if base.startswith('file://'):
40
 
            base = base[7:]
41
 
        # realpath is incompatible with symlinks. When we traverse
42
 
        # up we might be able to normpath stuff. RBC 20051003
43
 
        super(LocalTransport, self).__init__(
44
 
            os.path.normpath(os.path.abspath(base)))
45
 
 
46
 
    def should_cache(self):
47
 
        return False
48
 
 
49
 
    def clone(self, offset=None):
50
 
        """Return a new LocalTransport with root at self.base + offset
51
 
        Because the local filesystem does not require a connection, 
52
 
        we can just return a new object.
53
 
        """
54
 
        if offset is None:
55
 
            return LocalTransport(self.base)
56
 
        else:
57
 
            return LocalTransport(self.abspath(offset))
58
 
 
59
 
    def abspath(self, relpath):
60
 
        """Return the full url to the given relative path.
61
 
        This can be supplied with a string or a list
62
 
        """
63
 
        if isinstance(relpath, basestring):
64
 
            relpath = [relpath]
65
 
        return os.path.join(self.base, *relpath)
66
 
 
67
 
    def relpath(self, abspath):
68
 
        """Return the local path portion from a given absolute path.
69
 
        """
70
 
        from bzrlib.osutils import relpath
71
 
        return relpath(self.base, abspath)
72
 
 
73
 
    def has(self, relpath):
74
 
        return os.access(self.abspath(relpath), os.F_OK)
75
 
 
76
 
    def get(self, relpath):
77
 
        """Get the file at the given relative path.
78
 
 
79
 
        :param relpath: The relative path to the file
80
 
        """
81
 
        try:
82
 
            path = self.abspath(relpath)
83
 
            return open(path, 'rb')
84
 
        except IOError,e:
85
 
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
86
 
                raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
87
 
            raise LocalTransportError(orig_error=e)
88
 
 
89
 
    def put(self, relpath, f):
90
 
        """Copy the file-like or string object into the location.
91
 
 
92
 
        :param relpath: Location to put the contents, relative to base.
93
 
        :param f:       File-like or string object.
94
 
        """
95
 
        from bzrlib.atomicfile import AtomicFile
96
 
 
97
 
        try:
98
 
            path = self.abspath(relpath)
99
 
            fp = AtomicFile(path, 'wb')
100
 
        except IOError, e:
101
 
            if e.errno == errno.ENOENT:
102
 
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
103
 
            raise LocalTransportError(orig_error=e)
104
 
        try:
105
 
            self._pump(f, fp)
106
 
            fp.commit()
107
 
        finally:
108
 
            fp.close()
109
 
 
110
 
    def iter_files_recursive(self):
111
 
        """Iter the relative paths of files in the transports sub-tree."""
112
 
        queue = list(self.list_dir('.'))
113
 
        while queue:
114
 
            relpath = queue.pop(0)
115
 
            st = self.stat(relpath)
116
 
            if S_ISDIR(st[ST_MODE]):
117
 
                for i, basename in enumerate(self.list_dir(relpath)):
118
 
                    queue.insert(i, relpath+'/'+basename)
119
 
            else:
120
 
                yield relpath
121
 
 
122
 
    def mkdir(self, relpath):
123
 
        """Create a directory at the given path."""
124
 
        try:
125
 
            os.mkdir(self.abspath(relpath))
126
 
        except OSError,e:
127
 
            if e.errno == errno.EEXIST:
128
 
                raise FileExists(orig_error=e)
129
 
            elif e.errno == errno.ENOENT:
130
 
                raise NoSuchFile(orig_error=e)
131
 
            raise LocalTransportError(orig_error=e)
132
 
 
133
 
    def append(self, relpath, f):
134
 
        """Append the text in the file-like object into the final
135
 
        location.
136
 
        """
137
 
        fp = open(self.abspath(relpath), 'ab')
138
 
        self._pump(f, fp)
139
 
 
140
 
    def copy(self, rel_from, rel_to):
141
 
        """Copy the item at rel_from to the location at rel_to"""
142
 
        import shutil
143
 
        path_from = self.abspath(rel_from)
144
 
        path_to = self.abspath(rel_to)
145
 
        try:
146
 
            shutil.copy(path_from, path_to)
147
 
        except OSError,e:
148
 
            raise LocalTransportError(orig_error=e)
149
 
 
150
 
    def move(self, rel_from, rel_to):
151
 
        """Move the item at rel_from to the location at rel_to"""
152
 
        path_from = self.abspath(rel_from)
153
 
        path_to = self.abspath(rel_to)
154
 
 
155
 
        try:
156
 
            os.rename(path_from, path_to)
157
 
        except OSError,e:
158
 
            raise LocalTransportError(orig_error=e)
159
 
 
160
 
    def delete(self, relpath):
161
 
        """Delete the item at relpath"""
162
 
        try:
163
 
            os.remove(self.abspath(relpath))
164
 
        except OSError,e:
165
 
            raise LocalTransportError(orig_error=e)
166
 
 
167
 
    def copy_to(self, relpaths, other, pb=None):
168
 
        """Copy a set of entries from self into another Transport.
169
 
 
170
 
        :param relpaths: A list/generator of entries to be copied.
171
 
        """
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 :(
176
 
            import shutil
177
 
 
178
 
            total = self._get_total(relpaths)
179
 
            count = 0
180
 
            for path in relpaths:
181
 
                self._update_pb(pb, 'copy-to', count, total)
182
 
                shutil.copy(self.abspath(path), other.abspath(path))
183
 
                count += 1
184
 
            return count
185
 
        else:
186
 
            return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
187
 
 
188
 
    def listable(self):
189
 
        """See Transport.listable."""
190
 
        return True
191
 
 
192
 
    def list_dir(self, relpath):
193
 
        """Return a list of all files at the given location.
194
 
        WARNING: many transports do not support this, so trying avoid using
195
 
        it if at all possible.
196
 
        """
197
 
        try:
198
 
            return os.listdir(self.abspath(relpath))
199
 
        except OSError,e:
200
 
            raise LocalTransportError(orig_error=e)
201
 
 
202
 
    def stat(self, relpath):
203
 
        """Return the stat information for a file.
204
 
        """
205
 
        try:
206
 
            return os.stat(self.abspath(relpath))
207
 
        except OSError,e:
208
 
            raise LocalTransportError(orig_error=e)
209
 
 
210
 
    def lock_read(self, relpath):
211
 
        """Lock the given file for shared (read) access.
212
 
        :return: A lock object, which should be passed to Transport.unlock()
213
 
        """
214
 
        from bzrlib.lock import ReadLock
215
 
        return ReadLock(self.abspath(relpath))
216
 
 
217
 
    def lock_write(self, relpath):
218
 
        """Lock the given file for exclusive (write) access.
219
 
        WARNING: many transports do not support this, so trying avoid using it
220
 
 
221
 
        :return: A lock object, which should be passed to Transport.unlock()
222
 
        """
223
 
        from bzrlib.lock import WriteLock
224
 
        return WriteLock(self.abspath(relpath))
225
 
 
226
 
 
227
 
class ScratchTransport(LocalTransport):
228
 
    """A transport that works in a temporary dir and cleans up after itself.
229
 
    
230
 
    The dir only exists for the lifetime of the Python object.
231
 
    Obviously you should not put anything precious in it.
232
 
    """
233
 
 
234
 
    def __init__(self, base=None):
235
 
        if base is None:
236
 
            base = tempfile.mkdtemp()
237
 
        super(ScratchTransport, self).__init__(base)
238
 
 
239
 
    def __del__(self):
240
 
        shutil.rmtree(self.base, ignore_errors=True)
241
 
        mutter("%r destroyed" % self)
242
 
 
243
 
# If nothing else matches, try the LocalTransport
244
 
register_transport(None, LocalTransport)
245
 
register_transport('file://', LocalTransport)