~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-06-06 11:04:45 UTC
  • mfrom: (1740.1.2 bzr.mbp.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060606110445-79d71fb5b8e9fa8e
(mbp,jamesh) show file times; doc

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
 
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
20
20
 
21
21
import os
22
22
import shutil
 
23
import sys
23
24
from stat import ST_MODE, S_ISDIR, ST_SIZE
24
25
import tempfile
25
26
import urllib
26
27
 
27
28
from bzrlib.trace import mutter
28
29
from bzrlib.transport import Transport, Server
29
 
from bzrlib.osutils import abspath, realpath, normpath, pathjoin, rename
 
30
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename, 
 
31
                            check_legal_path, rmtree)
30
32
 
31
33
 
32
34
class LocalTransport(Transport):
35
37
    def __init__(self, base):
36
38
        """Set the base path where files will be stored."""
37
39
        if base.startswith('file://'):
38
 
            base = base[7:]
 
40
            base = base[len('file://'):]
39
41
        # realpath is incompatible with symlinks. When we traverse
40
42
        # up we might be able to normpath stuff. RBC 20051003
41
43
        base = normpath(abspath(base))
57
59
            return LocalTransport(self.abspath(offset))
58
60
 
59
61
    def abspath(self, relpath):
60
 
        """Return the full url to the given relative URL.
61
 
        This can be supplied with a string or a list
62
 
        """
 
62
        """Return the full url to the given relative URL."""
63
63
        assert isinstance(relpath, basestring), (type(relpath), relpath)
64
 
        return pathjoin(self.base, urllib.unquote(relpath))
 
64
        result = normpath(pathjoin(self.base, urllib.unquote(relpath)))
 
65
        #if result[-1] != '/':
 
66
        #    result += '/'
 
67
        return result
65
68
 
66
69
    def relpath(self, abspath):
67
70
        """Return the local path portion from a given absolute path.
68
71
        """
69
 
        from bzrlib.osutils import relpath
 
72
        from bzrlib.osutils import relpath, strip_trailing_slash
70
73
        if abspath is None:
71
74
            abspath = u'.'
72
 
        if abspath.endswith('/'):
73
 
            abspath = abspath[:-1]
74
 
        return relpath(self.base[:-1], abspath)
 
75
 
 
76
        return relpath(strip_trailing_slash(self.base), 
 
77
                       strip_trailing_slash(abspath))
75
78
 
76
79
    def has(self, relpath):
77
80
        return os.access(self.abspath(relpath), os.F_OK)
98
101
        path = relpath
99
102
        try:
100
103
            path = self.abspath(relpath)
 
104
            check_legal_path(path)
101
105
            fp = AtomicFile(path, 'wb', new_mode=mode)
102
106
        except (IOError, OSError),e:
103
107
            self._translate_error(e, path)
111
115
        """Iter the relative paths of files in the transports sub-tree."""
112
116
        queue = list(self.list_dir(u'.'))
113
117
        while queue:
114
 
            relpath = urllib.quote(queue.pop(0))
 
118
            relpath = queue.pop(0)
115
119
            st = self.stat(relpath)
116
120
            if S_ISDIR(st[ST_MODE]):
117
121
                for i, basename in enumerate(self.list_dir(relpath)):
130
134
        except (IOError, OSError),e:
131
135
            self._translate_error(e, path)
132
136
 
133
 
    def append(self, relpath, f):
 
137
    def append(self, relpath, f, mode=None):
134
138
        """Append the text in the file-like object into the final
135
139
        location.
136
140
        """
137
141
        try:
138
142
            fp = open(self.abspath(relpath), 'ab')
 
143
            if mode is not None:
 
144
                os.chmod(self.abspath(relpath), mode)
139
145
        except (IOError, OSError),e:
140
146
            self._translate_error(e, relpath)
 
147
        # win32 workaround (tell on an unwritten file returns 0)
 
148
        fp.seek(0, 2)
 
149
        result = fp.tell()
141
150
        self._pump(f, fp)
 
151
        return result
142
152
 
143
153
    def copy(self, rel_from, rel_to):
144
154
        """Copy the item at rel_from to the location at rel_to"""
145
 
        import shutil
146
155
        path_from = self.abspath(rel_from)
147
156
        path_to = self.abspath(rel_to)
148
157
        try:
151
160
            # TODO: What about path_to?
152
161
            self._translate_error(e, path_from)
153
162
 
 
163
    def rename(self, rel_from, rel_to):
 
164
        path_from = self.abspath(rel_from)
 
165
        try:
 
166
            # *don't* call bzrlib.osutils.rename, because we want to 
 
167
            # detect errors on rename
 
168
            os.rename(path_from, self.abspath(rel_to))
 
169
        except (IOError, OSError),e:
 
170
            # TODO: What about path_to?
 
171
            self._translate_error(e, path_from)
 
172
 
154
173
    def move(self, rel_from, rel_to):
155
174
        """Move the item at rel_from to the location at rel_to"""
156
175
        path_from = self.abspath(rel_from)
157
176
        path_to = self.abspath(rel_to)
158
177
 
159
178
        try:
 
179
            # this version will delete the destination if necessary
160
180
            rename(path_from, path_to)
161
181
        except (IOError, OSError),e:
162
182
            # TODO: What about path_to?
181
201
            # Both from & to are on the local filesystem
182
202
            # Unfortunately, I can't think of anything faster than just
183
203
            # copying them across, one by one :(
184
 
            import shutil
185
 
 
186
204
            total = self._get_total(relpaths)
187
205
            count = 0
188
206
            for path in relpaths:
209
227
        WARNING: many transports do not support this, so trying avoid using
210
228
        it if at all possible.
211
229
        """
212
 
        path = relpath
 
230
        path = self.abspath(relpath)
213
231
        try:
214
 
            path = self.abspath(relpath)
215
 
            return os.listdir(path)
216
 
        except (IOError, OSError),e:
 
232
            return [urllib.quote(entry) for entry in os.listdir(path)]
 
233
        except (IOError, OSError), e:
217
234
            self._translate_error(e, path)
218
235
 
219
236
    def stat(self, relpath):
247
264
        from bzrlib.lock import WriteLock
248
265
        return WriteLock(self.abspath(relpath))
249
266
 
 
267
    def rmdir(self, relpath):
 
268
        """See Transport.rmdir."""
 
269
        path = relpath
 
270
        try:
 
271
            path = self.abspath(relpath)
 
272
            os.rmdir(path)
 
273
        except (IOError, OSError),e:
 
274
            self._translate_error(e, path)
 
275
 
 
276
    def _can_roundtrip_unix_modebits(self):
 
277
        if sys.platform == 'win32':
 
278
            # anyone else?
 
279
            return False
 
280
        else:
 
281
            return True
 
282
 
250
283
 
251
284
class ScratchTransport(LocalTransport):
252
285
    """A transport that works in a temporary dir and cleans up after itself.
261
294
        super(ScratchTransport, self).__init__(base)
262
295
 
263
296
    def __del__(self):
264
 
        shutil.rmtree(self.base, ignore_errors=True)
 
297
        rmtree(self.base, ignore_errors=True)
265
298
        mutter("%r destroyed" % self)
266
299
 
267
300