~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

- store revision properties in revision xml

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Transport for the local filesystem.
18
 
 
19
 
This is a fairly thin wrapper on regular file IO."""
20
 
 
21
 
import os
22
 
import shutil
23
 
from stat import ST_MODE, S_ISDIR, ST_SIZE
24
 
import tempfile
25
 
import urllib
26
 
 
27
 
from bzrlib.trace import mutter
28
 
from bzrlib.transport import Transport
29
 
from bzrlib.osutils import abspath
30
 
 
 
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
31
25
 
32
26
class LocalTransport(Transport):
33
27
    """This is the transport agent for local filesystem access."""
39
33
        # realpath is incompatible with symlinks. When we traverse
40
34
        # up we might be able to normpath stuff. RBC 20051003
41
35
        super(LocalTransport, self).__init__(
42
 
            os.path.normpath(abspath(base)))
 
36
            os.path.normpath(os.path.abspath(base)))
43
37
 
44
38
    def should_cache(self):
45
39
        return False
55
49
            return LocalTransport(self.abspath(offset))
56
50
 
57
51
    def abspath(self, relpath):
58
 
        """Return the full url to the given relative URL.
 
52
        """Return the full url to the given relative path.
59
53
        This can be supplied with a string or a list
60
54
        """
61
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
62
 
        return os.path.join(self.base, urllib.unquote(relpath))
 
55
        if isinstance(relpath, basestring):
 
56
            relpath = [relpath]
 
57
        return os.path.join(self.base, *relpath)
63
58
 
64
59
    def relpath(self, abspath):
65
60
        """Return the local path portion from a given absolute path.
66
61
        """
67
 
        from bzrlib.osutils import relpath
68
 
        if abspath is None:
69
 
            abspath = u'.'
70
 
        return relpath(self.base, abspath)
 
62
        from bzrlib.branch import _relpath
 
63
        return _relpath(self.base, abspath)
71
64
 
72
65
    def has(self, relpath):
73
66
        return os.access(self.abspath(relpath), os.F_OK)
80
73
        try:
81
74
            path = self.abspath(relpath)
82
75
            return open(path, 'rb')
83
 
        except (IOError, OSError),e:
84
 
            self._translate_error(e, path)
 
76
        except IOError,e:
 
77
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
 
78
                raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
 
79
            raise LocalTransportError(orig_error=e)
 
80
 
 
81
    def get_partial(self, relpath, start, length=None):
 
82
        """Get just part of a file.
 
83
 
 
84
        :param relpath: Path to the file, relative to base
 
85
        :param start: The starting position to read from
 
86
        :param length: The length to read. A length of None indicates
 
87
                       read to the end of the file.
 
88
        :return: A file-like object containing at least the specified bytes.
 
89
                 Some implementations may return objects which can be read
 
90
                 past this length, but this is not guaranteed.
 
91
        """
 
92
        # LocalTransport.get_partial() doesn't care about the length
 
93
        # argument, because it is using a local file, and thus just
 
94
        # returns the file seek'ed to the appropriate location.
 
95
        try:
 
96
            path = self.abspath(relpath)
 
97
            f = open(path, 'rb')
 
98
            f.seek(start, 0)
 
99
            return f
 
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)
85
104
 
86
105
    def put(self, relpath, f):
87
106
        """Copy the file-like or string object into the location.
91
110
        """
92
111
        from bzrlib.atomicfile import AtomicFile
93
112
 
94
 
        path = relpath
95
113
        try:
96
114
            path = self.abspath(relpath)
97
115
            fp = AtomicFile(path, 'wb')
98
 
        except (IOError, OSError),e:
99
 
            self._translate_error(e, path)
 
116
        except IOError, e:
 
117
            if e.errno == errno.ENOENT:
 
118
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
 
119
            raise LocalTransportError(orig_error=e)
100
120
        try:
101
121
            self._pump(f, fp)
102
122
            fp.commit()
103
123
        finally:
104
124
            fp.close()
105
125
 
106
 
    def iter_files_recursive(self):
107
 
        """Iter the relative paths of files in the transports sub-tree."""
108
 
        queue = list(self.list_dir(u'.'))
109
 
        while queue:
110
 
            relpath = urllib.quote(queue.pop(0))
111
 
            st = self.stat(relpath)
112
 
            if S_ISDIR(st[ST_MODE]):
113
 
                for i, basename in enumerate(self.list_dir(relpath)):
114
 
                    queue.insert(i, relpath+'/'+basename)
115
 
            else:
116
 
                yield relpath
117
 
 
118
126
    def mkdir(self, relpath):
119
127
        """Create a directory at the given path."""
120
 
        path = relpath
121
128
        try:
122
 
            path = self.abspath(relpath)
123
 
            os.mkdir(path)
124
 
        except (IOError, OSError),e:
125
 
            self._translate_error(e, path)
 
129
            os.mkdir(self.abspath(relpath))
 
130
        except OSError,e:
 
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
136
 
127
137
    def append(self, relpath, f):
128
138
        """Append the text in the file-like object into the final
138
148
        path_to = self.abspath(rel_to)
139
149
        try:
140
150
            shutil.copy(path_from, path_to)
141
 
        except (IOError, OSError),e:
142
 
            # TODO: What about path_to?
143
 
            self._translate_error(e, path_from)
 
151
        except OSError,e:
 
152
            raise LocalTransportError(orig_error=e)
144
153
 
145
154
    def move(self, rel_from, rel_to):
146
155
        """Move the item at rel_from to the location at rel_to"""
149
158
 
150
159
        try:
151
160
            os.rename(path_from, path_to)
152
 
        except (IOError, OSError),e:
153
 
            # TODO: What about path_to?
154
 
            self._translate_error(e, path_from)
 
161
        except OSError,e:
 
162
            raise LocalTransportError(orig_error=e)
155
163
 
156
164
    def delete(self, relpath):
157
165
        """Delete the item at relpath"""
158
 
        path = relpath
159
166
        try:
160
 
            path = self.abspath(relpath)
161
 
            os.remove(path)
162
 
        except (IOError, OSError),e:
163
 
            # TODO: What about path_to?
164
 
            self._translate_error(e, path)
 
167
            os.remove(self.abspath(relpath))
 
168
        except OSError,e:
 
169
            raise LocalTransportError(orig_error=e)
165
170
 
166
171
    def copy_to(self, relpaths, other, pb=None):
167
172
        """Copy a set of entries from self into another Transport.
178
183
            count = 0
179
184
            for path in relpaths:
180
185
                self._update_pb(pb, 'copy-to', count, total)
181
 
                try:
182
 
                    shutil.copy(self.abspath(path), other.abspath(path))
183
 
                except (IOError, OSError),e:
184
 
                    self._translate_error(e, path)
 
186
                shutil.copy(self.abspath(path), other.abspath(path))
185
187
                count += 1
186
188
            return count
187
189
        else:
196
198
        WARNING: many transports do not support this, so trying avoid using
197
199
        it if at all possible.
198
200
        """
199
 
        path = relpath
200
201
        try:
201
 
            path = self.abspath(relpath)
202
 
            return os.listdir(path)
203
 
        except (IOError, OSError),e:
204
 
            self._translate_error(e, path)
 
202
            return os.listdir(self.abspath(relpath))
 
203
        except OSError,e:
 
204
            raise LocalTransportError(orig_error=e)
205
205
 
206
206
    def stat(self, relpath):
207
207
        """Return the stat information for a file.
208
208
        """
209
 
        path = relpath
210
209
        try:
211
 
            path = self.abspath(relpath)
212
 
            return os.stat(path)
213
 
        except (IOError, OSError),e:
214
 
            self._translate_error(e, path)
 
210
            return os.stat(self.abspath(relpath))
 
211
        except OSError,e:
 
212
            raise LocalTransportError(orig_error=e)
215
213
 
216
214
    def lock_read(self, relpath):
217
215
        """Lock the given file for shared (read) access.
229
227
        from bzrlib.lock import WriteLock
230
228
        return WriteLock(self.abspath(relpath))
231
229
 
232
 
 
233
 
class ScratchTransport(LocalTransport):
234
 
    """A transport that works in a temporary dir and cleans up after itself.
235
 
    
236
 
    The dir only exists for the lifetime of the Python object.
237
 
    Obviously you should not put anything precious in it.
238
 
    """
239
 
 
240
 
    def __init__(self, base=None):
241
 
        if base is None:
242
 
            base = tempfile.mkdtemp()
243
 
        super(ScratchTransport, self).__init__(base)
244
 
 
245
 
    def __del__(self):
246
 
        shutil.rmtree(self.base, ignore_errors=True)
247
 
        mutter("%r destroyed" % self)
 
230
# If nothing else matches, try the LocalTransport
 
231
register_transport(None, LocalTransport)
 
232
register_transport('file://', LocalTransport)