~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Robert Collins
  • Date: 2006-05-16 06:45:43 UTC
  • mto: (1713.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 1714.
  • Revision ID: robertc@robertcollins.net-20060516064543-5cb7cc63047ba98b
Start on bench_add, an add benchtest.

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.
20
 
"""
 
19
This is a fairly thin wrapper on regular file IO."""
21
20
 
22
21
import os
23
22
import shutil
24
23
import sys
25
24
from stat import ST_MODE, S_ISDIR, ST_SIZE
26
25
import tempfile
 
26
import urllib
27
27
 
 
28
from bzrlib.trace import mutter
 
29
from bzrlib.transport import Transport, Server
28
30
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename, 
29
31
                            check_legal_path, rmtree)
30
 
from bzrlib.symbol_versioning import warn
31
 
from bzrlib.trace import mutter
32
 
from bzrlib.transport import Transport, Server
33
 
import bzrlib.urlutils as urlutils
34
32
 
35
33
 
36
34
class LocalTransport(Transport):
38
36
 
39
37
    def __init__(self, base):
40
38
        """Set the base path where files will be stored."""
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)
 
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))
50
44
        if base[-1] != '/':
51
45
            base = base + '/'
52
46
        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)
55
47
 
56
48
    def should_cache(self):
57
49
        return False
66
58
        else:
67
59
            return LocalTransport(self.abspath(offset))
68
60
 
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
 
 
80
61
    def abspath(self, relpath):
81
62
        """Return the full url to the given relative URL."""
82
 
        # TODO: url escape the result. RBC 20060523.
83
63
        assert isinstance(relpath, basestring), (type(relpath), relpath)
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)
 
64
        result = normpath(pathjoin(self.base, urllib.unquote(relpath)))
 
65
        #if result[-1] != '/':
 
66
        #    result += '/'
 
67
        return result
103
68
 
104
69
    def relpath(self, abspath):
105
70
        """Return the local path portion from a given absolute path.
106
71
        """
 
72
        from bzrlib.osutils import relpath, strip_trailing_slash
107
73
        if abspath is None:
108
74
            abspath = u'.'
109
75
 
110
 
        return urlutils.file_relpath(
111
 
            urlutils.strip_trailing_slash(self.base), 
112
 
            urlutils.strip_trailing_slash(abspath))
 
76
        return relpath(strip_trailing_slash(self.base), 
 
77
                       strip_trailing_slash(abspath))
113
78
 
114
79
    def has(self, relpath):
115
 
        return os.access(self._abspath(relpath), os.F_OK)
 
80
        return os.access(self.abspath(relpath), os.F_OK)
116
81
 
117
82
    def get(self, relpath):
118
83
        """Get the file at the given relative path.
120
85
        :param relpath: The relative path to the file
121
86
        """
122
87
        try:
123
 
            path = self._abspath(relpath)
 
88
            path = self.abspath(relpath)
124
89
            return open(path, 'rb')
125
90
        except (IOError, OSError),e:
126
91
            self._translate_error(e, path)
135
100
 
136
101
        path = relpath
137
102
        try:
138
 
            path = self._abspath(relpath)
 
103
            path = self.abspath(relpath)
139
104
            check_legal_path(path)
140
105
            fp = AtomicFile(path, 'wb', new_mode=mode)
141
106
        except (IOError, OSError),e:
162
127
        """Create a directory at the given path."""
163
128
        path = relpath
164
129
        try:
165
 
            path = self._abspath(relpath)
 
130
            path = self.abspath(relpath)
166
131
            os.mkdir(path)
167
132
            if mode is not None:
168
133
                os.chmod(path, mode)
170
135
            self._translate_error(e, path)
171
136
 
172
137
    def append(self, relpath, f, mode=None):
173
 
        """Append the text in the file-like object into the final location."""
174
 
        abspath = self._abspath(relpath)
175
 
        fp = None
 
138
        """Append the text in the file-like object into the final
 
139
        location.
 
140
        """
176
141
        try:
177
 
            try:
178
 
                fp = open(abspath, 'ab')
179
 
                # FIXME should we really be chmodding every time ? RBC 20060523
180
 
                if mode is not None:
181
 
                    os.chmod(abspath, mode)
182
 
            except (IOError, OSError),e:
183
 
                self._translate_error(e, relpath)
184
 
            # win32 workaround (tell on an unwritten file returns 0)
185
 
            fp.seek(0, 2)
186
 
            result = fp.tell()
187
 
            self._pump(f, fp)
188
 
        finally:
189
 
            if fp is not None:
190
 
                fp.close()
 
142
            fp = open(self.abspath(relpath), 'ab')
 
143
            if mode is not None:
 
144
                os.chmod(self.abspath(relpath), mode)
 
145
        except (IOError, OSError),e:
 
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()
 
150
        self._pump(f, fp)
191
151
        return result
192
152
 
193
153
    def copy(self, rel_from, rel_to):
194
154
        """Copy the item at rel_from to the location at rel_to"""
195
 
        path_from = self._abspath(rel_from)
196
 
        path_to = self._abspath(rel_to)
 
155
        path_from = self.abspath(rel_from)
 
156
        path_to = self.abspath(rel_to)
197
157
        try:
198
158
            shutil.copy(path_from, path_to)
199
159
        except (IOError, OSError),e:
201
161
            self._translate_error(e, path_from)
202
162
 
203
163
    def rename(self, rel_from, rel_to):
204
 
        path_from = self._abspath(rel_from)
 
164
        path_from = self.abspath(rel_from)
205
165
        try:
206
166
            # *don't* call bzrlib.osutils.rename, because we want to 
207
167
            # detect errors on rename
208
 
            os.rename(path_from, self._abspath(rel_to))
 
168
            os.rename(path_from, self.abspath(rel_to))
209
169
        except (IOError, OSError),e:
210
170
            # TODO: What about path_to?
211
171
            self._translate_error(e, path_from)
212
172
 
213
173
    def move(self, rel_from, rel_to):
214
174
        """Move the item at rel_from to the location at rel_to"""
215
 
        path_from = self._abspath(rel_from)
216
 
        path_to = self._abspath(rel_to)
 
175
        path_from = self.abspath(rel_from)
 
176
        path_to = self.abspath(rel_to)
217
177
 
218
178
        try:
219
179
            # this version will delete the destination if necessary
226
186
        """Delete the item at relpath"""
227
187
        path = relpath
228
188
        try:
229
 
            path = self._abspath(relpath)
 
189
            path = self.abspath(relpath)
230
190
            os.remove(path)
231
191
        except (IOError, OSError),e:
 
192
            # TODO: What about path_to?
232
193
            self._translate_error(e, path)
233
194
 
234
195
    def copy_to(self, relpaths, other, mode=None, pb=None):
245
206
            for path in relpaths:
246
207
                self._update_pb(pb, 'copy-to', count, total)
247
208
                try:
248
 
                    mypath = self._abspath(path)
249
 
                    otherpath = other._abspath(path)
 
209
                    mypath = self.abspath(path)
 
210
                    otherpath = other.abspath(path)
250
211
                    shutil.copy(mypath, otherpath)
251
212
                    if mode is not None:
252
213
                        os.chmod(otherpath, mode)
266
227
        WARNING: many transports do not support this, so trying avoid using
267
228
        it if at all possible.
268
229
        """
269
 
        path = self._abspath(relpath)
 
230
        path = self.abspath(relpath)
270
231
        try:
271
 
            return [urlutils.escape(entry) for entry in os.listdir(path)]
 
232
            return [urllib.quote(entry) for entry in os.listdir(path)]
272
233
        except (IOError, OSError), e:
273
234
            self._translate_error(e, path)
274
235
 
277
238
        """
278
239
        path = relpath
279
240
        try:
280
 
            path = self._abspath(relpath)
 
241
            path = self.abspath(relpath)
281
242
            return os.stat(path)
282
243
        except (IOError, OSError),e:
283
244
            self._translate_error(e, path)
289
250
        from bzrlib.lock import ReadLock
290
251
        path = relpath
291
252
        try:
292
 
            path = self._abspath(relpath)
 
253
            path = self.abspath(relpath)
293
254
            return ReadLock(path)
294
255
        except (IOError, OSError), e:
295
256
            self._translate_error(e, path)
301
262
        :return: A lock object, which should be passed to Transport.unlock()
302
263
        """
303
264
        from bzrlib.lock import WriteLock
304
 
        return WriteLock(self._abspath(relpath))
 
265
        return WriteLock(self.abspath(relpath))
305
266
 
306
267
    def rmdir(self, relpath):
307
268
        """See Transport.rmdir."""
308
269
        path = relpath
309
270
        try:
310
 
            path = self._abspath(relpath)
 
271
            path = self.abspath(relpath)
311
272
            os.rmdir(path)
312
273
        except (IOError, OSError),e:
313
274
            self._translate_error(e, path)
320
281
            return True
321
282
 
322
283
 
 
284
class ScratchTransport(LocalTransport):
 
285
    """A transport that works in a temporary dir and cleans up after itself.
 
286
    
 
287
    The dir only exists for the lifetime of the Python object.
 
288
    Obviously you should not put anything precious in it.
 
289
    """
 
290
 
 
291
    def __init__(self, base=None):
 
292
        if base is None:
 
293
            base = tempfile.mkdtemp()
 
294
        super(ScratchTransport, self).__init__(base)
 
295
 
 
296
    def __del__(self):
 
297
        rmtree(self.base, ignore_errors=True)
 
298
        mutter("%r destroyed" % self)
 
299
 
 
300
 
323
301
class LocalRelpathServer(Server):
324
302
    """A pretend server for local transports, using relpaths."""
325
303
 
341
319
 
342
320
    def get_url(self):
343
321
        """See Transport.Server.get_url."""
344
 
        return urlutils.local_path_to_url('')
 
322
        # FIXME: \ to / on windows
 
323
        return "file://%s" % os.path.abspath("")
345
324
 
346
325
 
347
326
def get_test_permutations():