~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-10 23:16:19 UTC
  • mfrom: (1759 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1761.
  • Revision ID: mbp@sourcefrog.net-20060610231619-05b997deeb005d02
[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)
47
54
 
48
55
    def should_cache(self):
49
56
        return False
58
65
        else:
59
66
            return LocalTransport(self.abspath(offset))
60
67
 
 
68
    def _abspath(self, relative_reference):
 
69
        """Return a path for use in os calls.
 
70
 
 
71
        Several assumptions are made:
 
72
         - relative_reference does not contain '..'
 
73
         - relative_reference is url escaped.
 
74
        """
 
75
        if relative_reference in ('.', ''):
 
76
            return self._local_base
 
77
        return self._local_base + urlutils.unescape(relative_reference)
 
78
 
61
79
    def abspath(self, relpath):
62
80
        """Return the full url to the given relative URL."""
 
81
        # TODO: url escape the result. RBC 20060523.
63
82
        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
 
83
        # jam 20060426 Using normpath on the real path, because that ensures
 
84
        #       proper handling of stuff like
 
85
        path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
 
86
        return urlutils.local_path_to_url(path)
 
87
 
 
88
    def local_abspath(self, relpath):
 
89
        """Transform the given relative path URL into the actual path on disk
 
90
 
 
91
        This function only exists for the LocalTransport, since it is
 
92
        the only one that has direct local access.
 
93
        This is mostly for stuff like WorkingTree which needs to know
 
94
        the local working directory.
 
95
        
 
96
        This function is quite expensive: it calls realpath which resolves
 
97
        symlinks.
 
98
        """
 
99
        absurl = self.abspath(relpath)
 
100
        # mutter(u'relpath %s => base: %s, absurl %s', relpath, self.base, absurl)
 
101
        return urlutils.local_path_from_url(absurl)
68
102
 
69
103
    def relpath(self, abspath):
70
104
        """Return the local path portion from a given absolute path.
71
105
        """
72
 
        from bzrlib.osutils import relpath, strip_trailing_slash
73
106
        if abspath is None:
74
107
            abspath = u'.'
75
108
 
76
 
        return relpath(strip_trailing_slash(self.base), 
77
 
                       strip_trailing_slash(abspath))
 
109
        return urlutils.file_relpath(
 
110
            urlutils.strip_trailing_slash(self.base), 
 
111
            urlutils.strip_trailing_slash(abspath))
78
112
 
79
113
    def has(self, relpath):
80
 
        return os.access(self.abspath(relpath), os.F_OK)
 
114
        return os.access(self._abspath(relpath), os.F_OK)
81
115
 
82
116
    def get(self, relpath):
83
117
        """Get the file at the given relative path.
85
119
        :param relpath: The relative path to the file
86
120
        """
87
121
        try:
88
 
            path = self.abspath(relpath)
 
122
            path = self._abspath(relpath)
89
123
            return open(path, 'rb')
90
124
        except (IOError, OSError),e:
91
125
            self._translate_error(e, path)
100
134
 
101
135
        path = relpath
102
136
        try:
103
 
            path = self.abspath(relpath)
 
137
            path = self._abspath(relpath)
104
138
            check_legal_path(path)
105
139
            fp = AtomicFile(path, 'wb', new_mode=mode)
106
140
        except (IOError, OSError),e:
127
161
        """Create a directory at the given path."""
128
162
        path = relpath
129
163
        try:
130
 
            path = self.abspath(relpath)
 
164
            path = self._abspath(relpath)
131
165
            os.mkdir(path)
132
166
            if mode is not None:
133
167
                os.chmod(path, mode)
135
169
            self._translate_error(e, path)
136
170
 
137
171
    def append(self, relpath, f, mode=None):
138
 
        """Append the text in the file-like object into the final
139
 
        location.
140
 
        """
 
172
        """Append the text in the file-like object into the final location."""
 
173
        abspath = self._abspath(relpath)
141
174
        try:
142
 
            fp = open(self.abspath(relpath), 'ab')
 
175
            fp = open(abspath, 'ab')
 
176
            # FIXME should we really be chmodding every time ? RBC 20060523
143
177
            if mode is not None:
144
 
                os.chmod(self.abspath(relpath), mode)
 
178
                os.chmod(abspath, mode)
145
179
        except (IOError, OSError),e:
146
180
            self._translate_error(e, relpath)
147
181
        # win32 workaround (tell on an unwritten file returns 0)
152
186
 
153
187
    def copy(self, rel_from, rel_to):
154
188
        """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)
 
189
        path_from = self._abspath(rel_from)
 
190
        path_to = self._abspath(rel_to)
157
191
        try:
158
192
            shutil.copy(path_from, path_to)
159
193
        except (IOError, OSError),e:
161
195
            self._translate_error(e, path_from)
162
196
 
163
197
    def rename(self, rel_from, rel_to):
164
 
        path_from = self.abspath(rel_from)
 
198
        path_from = self._abspath(rel_from)
165
199
        try:
166
200
            # *don't* call bzrlib.osutils.rename, because we want to 
167
201
            # detect errors on rename
168
 
            os.rename(path_from, self.abspath(rel_to))
 
202
            os.rename(path_from, self._abspath(rel_to))
169
203
        except (IOError, OSError),e:
170
204
            # TODO: What about path_to?
171
205
            self._translate_error(e, path_from)
172
206
 
173
207
    def move(self, rel_from, rel_to):
174
208
        """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)
 
209
        path_from = self._abspath(rel_from)
 
210
        path_to = self._abspath(rel_to)
177
211
 
178
212
        try:
179
213
            # this version will delete the destination if necessary
186
220
        """Delete the item at relpath"""
187
221
        path = relpath
188
222
        try:
189
 
            path = self.abspath(relpath)
 
223
            path = self._abspath(relpath)
190
224
            os.remove(path)
191
225
        except (IOError, OSError),e:
192
 
            # TODO: What about path_to?
193
226
            self._translate_error(e, path)
194
227
 
195
228
    def copy_to(self, relpaths, other, mode=None, pb=None):
206
239
            for path in relpaths:
207
240
                self._update_pb(pb, 'copy-to', count, total)
208
241
                try:
209
 
                    mypath = self.abspath(path)
210
 
                    otherpath = other.abspath(path)
 
242
                    mypath = self._abspath(path)
 
243
                    otherpath = other._abspath(path)
211
244
                    shutil.copy(mypath, otherpath)
212
245
                    if mode is not None:
213
246
                        os.chmod(otherpath, mode)
227
260
        WARNING: many transports do not support this, so trying avoid using
228
261
        it if at all possible.
229
262
        """
230
 
        path = self.abspath(relpath)
 
263
        path = self._abspath(relpath)
231
264
        try:
232
 
            return [urllib.quote(entry) for entry in os.listdir(path)]
 
265
            return [urlutils.escape(entry) for entry in os.listdir(path)]
233
266
        except (IOError, OSError), e:
234
267
            self._translate_error(e, path)
235
268
 
238
271
        """
239
272
        path = relpath
240
273
        try:
241
 
            path = self.abspath(relpath)
 
274
            path = self._abspath(relpath)
242
275
            return os.stat(path)
243
276
        except (IOError, OSError),e:
244
277
            self._translate_error(e, path)
250
283
        from bzrlib.lock import ReadLock
251
284
        path = relpath
252
285
        try:
253
 
            path = self.abspath(relpath)
 
286
            path = self._abspath(relpath)
254
287
            return ReadLock(path)
255
288
        except (IOError, OSError), e:
256
289
            self._translate_error(e, path)
262
295
        :return: A lock object, which should be passed to Transport.unlock()
263
296
        """
264
297
        from bzrlib.lock import WriteLock
265
 
        return WriteLock(self.abspath(relpath))
 
298
        return WriteLock(self._abspath(relpath))
266
299
 
267
300
    def rmdir(self, relpath):
268
301
        """See Transport.rmdir."""
269
302
        path = relpath
270
303
        try:
271
 
            path = self.abspath(relpath)
 
304
            path = self._abspath(relpath)
272
305
            os.rmdir(path)
273
306
        except (IOError, OSError),e:
274
307
            self._translate_error(e, path)
319
352
 
320
353
    def get_url(self):
321
354
        """See Transport.Server.get_url."""
322
 
        # FIXME: \ to / on windows
323
 
        return "file://%s" % os.path.abspath("")
 
355
        return urlutils.local_path_to_url('')
324
356
 
325
357
 
326
358
def get_test_permutations():