~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Martin Pool
  • Date: 2006-06-20 07:55:43 UTC
  • mfrom: (1798 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1799.
  • Revision ID: mbp@sourcefrog.net-20060620075543-b10f6575d4a4fa32
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Transport for the local filesystem.
18
18
 
19
 
This is a fairly thin wrapper on regular file IO."""
 
19
This is a fairly thin wrapper on regular file IO.
 
20
"""
20
21
 
21
22
import os
22
23
import shutil
23
24
import sys
24
25
from stat import ST_MODE, S_ISDIR, ST_SIZE
25
26
import tempfile
26
 
import urllib
27
27
 
 
28
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename, 
 
29
                            check_legal_path, rmtree)
 
30
from bzrlib.symbol_versioning import warn
28
31
from bzrlib.trace import mutter
29
32
from bzrlib.transport import Transport, Server
30
 
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename, 
31
 
                            check_legal_path, rmtree)
 
33
import bzrlib.urlutils as urlutils
32
34
 
33
35
 
34
36
class LocalTransport(Transport):
36
38
 
37
39
    def __init__(self, base):
38
40
        """Set the base path where files will be stored."""
39
 
        if base.startswith('file://'):
40
 
            base = base[len('file://'):]
41
 
        # realpath is incompatible with symlinks. When we traverse
42
 
        # up we might be able to normpath stuff. RBC 20051003
43
 
        base = normpath(abspath(base))
 
41
        if not base.startswith('file://'):
 
42
            warn("Instantiating LocalTransport with a filesystem path"
 
43
                " is deprecated as of bzr 0.8."
 
44
                " Please use bzrlib.transport.get_transport()"
 
45
                " or pass in a file:// url.",
 
46
                 DeprecationWarning,
 
47
                 stacklevel=2
 
48
                 )
 
49
            base = urlutils.local_path_to_url(base)
44
50
        if base[-1] != '/':
45
51
            base = base + '/'
46
52
        super(LocalTransport, self).__init__(base)
 
53
        self._local_base = urlutils.local_path_from_url(base)
 
54
        ## mutter("_local_base: %r => %r", base, self._local_base)
47
55
 
48
56
    def should_cache(self):
49
57
        return False
58
66
        else:
59
67
            return LocalTransport(self.abspath(offset))
60
68
 
 
69
    def _abspath(self, relative_reference):
 
70
        """Return a path for use in os calls.
 
71
 
 
72
        Several assumptions are made:
 
73
         - relative_reference does not contain '..'
 
74
         - relative_reference is url escaped.
 
75
        """
 
76
        if relative_reference in ('.', ''):
 
77
            return self._local_base
 
78
        return self._local_base + urlutils.unescape(relative_reference)
 
79
 
61
80
    def abspath(self, relpath):
62
81
        """Return the full url to the given relative URL."""
 
82
        # TODO: url escape the result. RBC 20060523.
63
83
        assert isinstance(relpath, basestring), (type(relpath), relpath)
64
 
        result = normpath(pathjoin(self.base, urllib.unquote(relpath)))
65
 
        #if result[-1] != '/':
66
 
        #    result += '/'
67
 
        return result
 
84
        # jam 20060426 Using normpath on the real path, because that ensures
 
85
        #       proper handling of stuff like
 
86
        path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
 
87
        return urlutils.local_path_to_url(path)
 
88
 
 
89
    def local_abspath(self, relpath):
 
90
        """Transform the given relative path URL into the actual path on disk
 
91
 
 
92
        This function only exists for the LocalTransport, since it is
 
93
        the only one that has direct local access.
 
94
        This is mostly for stuff like WorkingTree which needs to know
 
95
        the local working directory.
 
96
        
 
97
        This function is quite expensive: it calls realpath which resolves
 
98
        symlinks.
 
99
        """
 
100
        absurl = self.abspath(relpath)
 
101
        # mutter(u'relpath %s => base: %s, absurl %s', relpath, self.base, absurl)
 
102
        return urlutils.local_path_from_url(absurl)
68
103
 
69
104
    def relpath(self, abspath):
70
105
        """Return the local path portion from a given absolute path.
71
106
        """
72
 
        from bzrlib.osutils import relpath, strip_trailing_slash
73
107
        if abspath is None:
74
108
            abspath = u'.'
75
109
 
76
 
        return relpath(strip_trailing_slash(self.base), 
77
 
                       strip_trailing_slash(abspath))
 
110
        return urlutils.file_relpath(
 
111
            urlutils.strip_trailing_slash(self.base), 
 
112
            urlutils.strip_trailing_slash(abspath))
78
113
 
79
114
    def has(self, relpath):
80
 
        return os.access(self.abspath(relpath), os.F_OK)
 
115
        return os.access(self._abspath(relpath), os.F_OK)
81
116
 
82
117
    def get(self, relpath):
83
118
        """Get the file at the given relative path.
85
120
        :param relpath: The relative path to the file
86
121
        """
87
122
        try:
88
 
            path = self.abspath(relpath)
 
123
            path = self._abspath(relpath)
89
124
            return open(path, 'rb')
90
125
        except (IOError, OSError),e:
91
126
            self._translate_error(e, path)
100
135
 
101
136
        path = relpath
102
137
        try:
103
 
            path = self.abspath(relpath)
 
138
            path = self._abspath(relpath)
104
139
            check_legal_path(path)
105
140
            fp = AtomicFile(path, 'wb', new_mode=mode)
106
141
        except (IOError, OSError),e:
127
162
        """Create a directory at the given path."""
128
163
        path = relpath
129
164
        try:
130
 
            path = self.abspath(relpath)
 
165
            path = self._abspath(relpath)
131
166
            os.mkdir(path)
132
167
            if mode is not None:
133
168
                os.chmod(path, mode)
135
170
            self._translate_error(e, path)
136
171
 
137
172
    def append(self, relpath, f, mode=None):
138
 
        """Append the text in the file-like object into the final
139
 
        location.
140
 
        """
 
173
        """Append the text in the file-like object into the final location."""
 
174
        abspath = self._abspath(relpath)
141
175
        try:
142
 
            fp = open(self.abspath(relpath), 'ab')
 
176
            fp = open(abspath, 'ab')
 
177
            # FIXME should we really be chmodding every time ? RBC 20060523
143
178
            if mode is not None:
144
 
                os.chmod(self.abspath(relpath), mode)
 
179
                os.chmod(abspath, mode)
145
180
        except (IOError, OSError),e:
146
181
            self._translate_error(e, relpath)
147
182
        # win32 workaround (tell on an unwritten file returns 0)
152
187
 
153
188
    def copy(self, rel_from, rel_to):
154
189
        """Copy the item at rel_from to the location at rel_to"""
155
 
        path_from = self.abspath(rel_from)
156
 
        path_to = self.abspath(rel_to)
 
190
        path_from = self._abspath(rel_from)
 
191
        path_to = self._abspath(rel_to)
157
192
        try:
158
193
            shutil.copy(path_from, path_to)
159
194
        except (IOError, OSError),e:
161
196
            self._translate_error(e, path_from)
162
197
 
163
198
    def rename(self, rel_from, rel_to):
164
 
        path_from = self.abspath(rel_from)
 
199
        path_from = self._abspath(rel_from)
165
200
        try:
166
201
            # *don't* call bzrlib.osutils.rename, because we want to 
167
202
            # detect errors on rename
168
 
            os.rename(path_from, self.abspath(rel_to))
 
203
            os.rename(path_from, self._abspath(rel_to))
169
204
        except (IOError, OSError),e:
170
205
            # TODO: What about path_to?
171
206
            self._translate_error(e, path_from)
172
207
 
173
208
    def move(self, rel_from, rel_to):
174
209
        """Move the item at rel_from to the location at rel_to"""
175
 
        path_from = self.abspath(rel_from)
176
 
        path_to = self.abspath(rel_to)
 
210
        path_from = self._abspath(rel_from)
 
211
        path_to = self._abspath(rel_to)
177
212
 
178
213
        try:
179
214
            # this version will delete the destination if necessary
186
221
        """Delete the item at relpath"""
187
222
        path = relpath
188
223
        try:
189
 
            path = self.abspath(relpath)
 
224
            path = self._abspath(relpath)
190
225
            os.remove(path)
191
226
        except (IOError, OSError),e:
192
 
            # TODO: What about path_to?
193
227
            self._translate_error(e, path)
194
228
 
195
229
    def copy_to(self, relpaths, other, mode=None, pb=None):
206
240
            for path in relpaths:
207
241
                self._update_pb(pb, 'copy-to', count, total)
208
242
                try:
209
 
                    mypath = self.abspath(path)
210
 
                    otherpath = other.abspath(path)
 
243
                    mypath = self._abspath(path)
 
244
                    otherpath = other._abspath(path)
211
245
                    shutil.copy(mypath, otherpath)
212
246
                    if mode is not None:
213
247
                        os.chmod(otherpath, mode)
227
261
        WARNING: many transports do not support this, so trying avoid using
228
262
        it if at all possible.
229
263
        """
230
 
        path = self.abspath(relpath)
 
264
        path = self._abspath(relpath)
231
265
        try:
232
 
            return [urllib.quote(entry) for entry in os.listdir(path)]
 
266
            return [urlutils.escape(entry) for entry in os.listdir(path)]
233
267
        except (IOError, OSError), e:
234
268
            self._translate_error(e, path)
235
269
 
238
272
        """
239
273
        path = relpath
240
274
        try:
241
 
            path = self.abspath(relpath)
 
275
            path = self._abspath(relpath)
242
276
            return os.stat(path)
243
277
        except (IOError, OSError),e:
244
278
            self._translate_error(e, path)
250
284
        from bzrlib.lock import ReadLock
251
285
        path = relpath
252
286
        try:
253
 
            path = self.abspath(relpath)
 
287
            path = self._abspath(relpath)
254
288
            return ReadLock(path)
255
289
        except (IOError, OSError), e:
256
290
            self._translate_error(e, path)
262
296
        :return: A lock object, which should be passed to Transport.unlock()
263
297
        """
264
298
        from bzrlib.lock import WriteLock
265
 
        return WriteLock(self.abspath(relpath))
 
299
        return WriteLock(self._abspath(relpath))
266
300
 
267
301
    def rmdir(self, relpath):
268
302
        """See Transport.rmdir."""
269
303
        path = relpath
270
304
        try:
271
 
            path = self.abspath(relpath)
 
305
            path = self._abspath(relpath)
272
306
            os.rmdir(path)
273
307
        except (IOError, OSError),e:
274
308
            self._translate_error(e, path)
302
336
 
303
337
    def get_url(self):
304
338
        """See Transport.Server.get_url."""
305
 
        # FIXME: \ to / on windows
306
 
        return "file://%s" % os.path.abspath("")
 
339
        return urlutils.local_path_to_url('')
307
340
 
308
341
 
309
342
def get_test_permutations():