~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Martin Pool
  • Date: 2005-09-30 05:15:03 UTC
  • mto: (1185.14.2)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: mbp@sourcefrog.net-20050930051503-9c049325215ddd1c
- fix up Branch.open_downlevel for Transport

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
from bzrlib.transport import Transport, register_transport, \
 
20
    TransportError, NoSuchFile, FileExists
 
21
import os, errno
 
22
 
 
23
class LocalTransportError(TransportError):
 
24
    pass
 
25
 
 
26
class LocalTransport(Transport):
 
27
    """This is the transport agent for local filesystem access."""
 
28
 
 
29
    def __init__(self, base):
 
30
        """Set the base path where files will be stored."""
 
31
        if base.startswith('file://'):
 
32
            base = base[7:]
 
33
        super(LocalTransport, self).__init__(os.path.realpath(base))
 
34
 
 
35
    def should_cache(self):
 
36
        return False
 
37
 
 
38
    def clone(self, offset=None):
 
39
        """Return a new LocalTransport with root at self.base + offset
 
40
        Because the local filesystem does not require a connection, 
 
41
        we can just return a new object.
 
42
        """
 
43
        if offset is None:
 
44
            return LocalTransport(self.base)
 
45
        else:
 
46
            return LocalTransport(self.abspath(offset))
 
47
 
 
48
    def abspath(self, relpath):
 
49
        """Return the full url to the given relative path.
 
50
        This can be supplied with a string or a list
 
51
        """
 
52
        if isinstance(relpath, basestring):
 
53
            relpath = [relpath]
 
54
        return os.path.join(self.base, *relpath)
 
55
 
 
56
    def relpath(self, abspath):
 
57
        """Return the local path portion from a given absolute path.
 
58
        """
 
59
        from bzrlib.branch import _relpath
 
60
        return _relpath(self.base, abspath)
 
61
 
 
62
    def has(self, relpath):
 
63
        return os.access(self.abspath(relpath), os.F_OK)
 
64
 
 
65
    def get(self, relpath):
 
66
        """Get the file at the given relative path.
 
67
 
 
68
        :param relpath: The relative path to the file
 
69
        """
 
70
        try:
 
71
            path = self.abspath(relpath)
 
72
            return open(path, 'rb')
 
73
        except IOError,e:
 
74
            if e.errno == errno.ENOENT:
 
75
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
 
76
            raise LocalTransportError(orig_error=e)
 
77
 
 
78
    def get_partial(self, relpath, start, length=None):
 
79
        """Get just part of a file.
 
80
 
 
81
        :param relpath: Path to the file, relative to base
 
82
        :param start: The starting position to read from
 
83
        :param length: The length to read. A length of None indicates
 
84
                       read to the end of the file.
 
85
        :return: A file-like object containing at least the specified bytes.
 
86
                 Some implementations may return objects which can be read
 
87
                 past this length, but this is not guaranteed.
 
88
        """
 
89
        # LocalTransport.get_partial() doesn't care about the length
 
90
        # argument, because it is using a local file, and thus just
 
91
        # returns the file seek'ed to the appropriate location.
 
92
        try:
 
93
            path = self.abspath(relpath)
 
94
            f = open(path, 'rb')
 
95
            f.seek(start, 0)
 
96
            return f
 
97
        except IOError,e:
 
98
            if e.errno == errno.ENOENT:
 
99
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
 
100
            raise LocalTransportError(orig_error=e)
 
101
 
 
102
    def put(self, relpath, f):
 
103
        """Copy the file-like or string object into the location.
 
104
 
 
105
        :param relpath: Location to put the contents, relative to base.
 
106
        :param f:       File-like or string object.
 
107
        """
 
108
        from bzrlib.atomicfile import AtomicFile
 
109
 
 
110
        try:
 
111
            path = self.abspath(relpath)
 
112
            fp = AtomicFile(path, 'wb')
 
113
        except IOError, e:
 
114
            if e.errno == errno.ENOENT:
 
115
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
 
116
            raise LocalTransportError(orig_error=e)
 
117
        try:
 
118
            self._pump(f, fp)
 
119
            fp.commit()
 
120
        finally:
 
121
            fp.close()
 
122
 
 
123
    def mkdir(self, relpath):
 
124
        """Create a directory at the given path."""
 
125
        try:
 
126
            os.mkdir(self.abspath(relpath))
 
127
        except OSError,e:
 
128
            if e.errno == errno.EEXIST:
 
129
                raise FileExists(orig_error=e)
 
130
            elif e.errno == errno.ENOENT:
 
131
                raise NoSuchFile(orig_error=e)
 
132
            raise LocalTransportError(orig_error=e)
 
133
 
 
134
    def append(self, relpath, f):
 
135
        """Append the text in the file-like object into the final
 
136
        location.
 
137
        """
 
138
        fp = open(self.abspath(relpath), 'ab')
 
139
        self._pump(f, fp)
 
140
 
 
141
    def copy(self, rel_from, rel_to):
 
142
        """Copy the item at rel_from to the location at rel_to"""
 
143
        import shutil
 
144
        path_from = self.abspath(rel_from)
 
145
        path_to = self.abspath(rel_to)
 
146
        try:
 
147
            shutil.copy(path_from, path_to)
 
148
        except OSError,e:
 
149
            raise LocalTransportError(orig_error=e)
 
150
 
 
151
    def move(self, rel_from, rel_to):
 
152
        """Move the item at rel_from to the location at rel_to"""
 
153
        path_from = self.abspath(rel_from)
 
154
        path_to = self.abspath(rel_to)
 
155
 
 
156
        try:
 
157
            os.rename(path_from, path_to)
 
158
        except OSError,e:
 
159
            raise LocalTransportError(orig_error=e)
 
160
 
 
161
    def delete(self, relpath):
 
162
        """Delete the item at relpath"""
 
163
        try:
 
164
            os.remove(self.abspath(relpath))
 
165
        except OSError,e:
 
166
            raise LocalTransportError(orig_error=e)
 
167
 
 
168
    def copy_to(self, relpaths, other, pb=None):
 
169
        """Copy a set of entries from self into another Transport.
 
170
 
 
171
        :param relpaths: A list/generator of entries to be copied.
 
172
        """
 
173
        if isinstance(other, LocalTransport):
 
174
            # Both from & to are on the local filesystem
 
175
            # Unfortunately, I can't think of anything faster than just
 
176
            # copying them across, one by one :(
 
177
            import shutil
 
178
 
 
179
            total = self._get_total(relpaths)
 
180
            count = 0
 
181
            for path in relpaths:
 
182
                self._update_pb(pb, 'copy-to', count, total)
 
183
                shutil.copy(self.abspath(path), other.abspath(path))
 
184
                count += 1
 
185
            return count
 
186
        else:
 
187
            return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
 
188
 
 
189
 
 
190
    def list_dir(self, relpath):
 
191
        """Return a list of all files at the given location.
 
192
        WARNING: many transports do not support this, so trying avoid using
 
193
        it if at all possible.
 
194
        """
 
195
        try:
 
196
            return os.listdir(self.abspath(relpath))
 
197
        except OSError,e:
 
198
            raise LocalTransportError(orig_error=e)
 
199
 
 
200
    def stat(self, relpath):
 
201
        """Return the stat information for a file.
 
202
        """
 
203
        try:
 
204
            return os.stat(self.abspath(relpath))
 
205
        except OSError,e:
 
206
            raise LocalTransportError(orig_error=e)
 
207
 
 
208
    def lock_read(self, relpath):
 
209
        """Lock the given file for shared (read) access.
 
210
        :return: A lock object, which should be passed to Transport.unlock()
 
211
        """
 
212
        from bzrlib.lock import ReadLock
 
213
        return ReadLock(self.abspath(relpath))
 
214
 
 
215
    def lock_write(self, relpath):
 
216
        """Lock the given file for exclusive (write) access.
 
217
        WARNING: many transports do not support this, so trying avoid using it
 
218
 
 
219
        :return: A lock object, which should be passed to Transport.unlock()
 
220
        """
 
221
        from bzrlib.lock import WriteLock
 
222
        return WriteLock(self.abspath(relpath))
 
223
 
 
224
# If nothing else matches, try the LocalTransport
 
225
register_transport(None, LocalTransport)
 
226
register_transport('file://', LocalTransport)