/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: John Arbash Meinel
  • Date: 2006-09-05 19:58:39 UTC
  • mto: (1946.2.8 reduce-knit-churn)
  • mto: This revision was merged to the branch mainline in revision 1988.
  • Revision ID: john@arbash-meinel.com-20060905195839-307783fb130a341c
avoid some deprecation warnings in other parts of the code

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
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
 
 
22
import os
 
23
import shutil
 
24
import sys
 
25
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
 
26
import tempfile
 
27
 
 
28
from bzrlib import (
 
29
    atomicfile,
 
30
    osutils,
 
31
    urlutils,
 
32
    )
 
33
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename,
 
34
                            check_legal_path, rmtree)
 
35
from bzrlib.symbol_versioning import warn
 
36
from bzrlib.trace import mutter
 
37
from bzrlib.transport import Transport, Server
 
38
 
 
39
 
 
40
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
 
41
 
 
42
 
 
43
class LocalTransport(Transport):
 
44
    """This is the transport agent for local filesystem access."""
 
45
 
 
46
    def __init__(self, base):
 
47
        """Set the base path where files will be stored."""
 
48
        if not base.startswith('file://'):
 
49
            warn("Instantiating LocalTransport with a filesystem path"
 
50
                " is deprecated as of bzr 0.8."
 
51
                " Please use bzrlib.transport.get_transport()"
 
52
                " or pass in a file:// url.",
 
53
                 DeprecationWarning,
 
54
                 stacklevel=2
 
55
                 )
 
56
            base = urlutils.local_path_to_url(base)
 
57
        if base[-1] != '/':
 
58
            base = base + '/'
 
59
        super(LocalTransport, self).__init__(base)
 
60
        self._local_base = urlutils.local_path_from_url(base)
 
61
 
 
62
    def should_cache(self):
 
63
        return False
 
64
 
 
65
    def clone(self, offset=None):
 
66
        """Return a new LocalTransport with root at self.base + offset
 
67
        Because the local filesystem does not require a connection, 
 
68
        we can just return a new object.
 
69
        """
 
70
        if offset is None:
 
71
            return LocalTransport(self.base)
 
72
        else:
 
73
            return LocalTransport(self.abspath(offset))
 
74
 
 
75
    def _abspath(self, relative_reference):
 
76
        """Return a path for use in os calls.
 
77
 
 
78
        Several assumptions are made:
 
79
         - relative_reference does not contain '..'
 
80
         - relative_reference is url escaped.
 
81
        """
 
82
        if relative_reference in ('.', ''):
 
83
            return self._local_base
 
84
        return self._local_base + urlutils.unescape(relative_reference)
 
85
 
 
86
    def abspath(self, relpath):
 
87
        """Return the full url to the given relative URL."""
 
88
        # TODO: url escape the result. RBC 20060523.
 
89
        assert isinstance(relpath, basestring), (type(relpath), relpath)
 
90
        # jam 20060426 Using normpath on the real path, because that ensures
 
91
        #       proper handling of stuff like
 
92
        path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
 
93
        return urlutils.local_path_to_url(path)
 
94
 
 
95
    def local_abspath(self, relpath):
 
96
        """Transform the given relative path URL into the actual path on disk
 
97
 
 
98
        This function only exists for the LocalTransport, since it is
 
99
        the only one that has direct local access.
 
100
        This is mostly for stuff like WorkingTree which needs to know
 
101
        the local working directory.
 
102
        
 
103
        This function is quite expensive: it calls realpath which resolves
 
104
        symlinks.
 
105
        """
 
106
        absurl = self.abspath(relpath)
 
107
        # mutter(u'relpath %s => base: %s, absurl %s', relpath, self.base, absurl)
 
108
        return urlutils.local_path_from_url(absurl)
 
109
 
 
110
    def relpath(self, abspath):
 
111
        """Return the local path portion from a given absolute path.
 
112
        """
 
113
        if abspath is None:
 
114
            abspath = u'.'
 
115
 
 
116
        return urlutils.file_relpath(
 
117
            urlutils.strip_trailing_slash(self.base), 
 
118
            urlutils.strip_trailing_slash(abspath))
 
119
 
 
120
    def has(self, relpath):
 
121
        return os.access(self._abspath(relpath), os.F_OK)
 
122
 
 
123
    def get(self, relpath):
 
124
        """Get the file at the given relative path.
 
125
 
 
126
        :param relpath: The relative path to the file
 
127
        """
 
128
        try:
 
129
            path = self._abspath(relpath)
 
130
            return open(path, 'rb')
 
131
        except (IOError, OSError),e:
 
132
            self._translate_error(e, path)
 
133
 
 
134
    def put_file(self, relpath, f, mode=None):
 
135
        """Copy the file-like object into the location.
 
136
 
 
137
        :param relpath: Location to put the contents, relative to base.
 
138
        :param f:       File-like or string object.
 
139
        """
 
140
 
 
141
        path = relpath
 
142
        try:
 
143
            path = self._abspath(relpath)
 
144
            check_legal_path(path)
 
145
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
 
146
        except (IOError, OSError),e:
 
147
            self._translate_error(e, path)
 
148
        try:
 
149
            self._pump(f, fp)
 
150
            fp.commit()
 
151
        finally:
 
152
            fp.close()
 
153
 
 
154
    def put_bytes(self, relpath, bytes, mode=None):
 
155
        """Copy the string into the location.
 
156
 
 
157
        :param relpath: Location to put the contents, relative to base.
 
158
        :param bytes:   String
 
159
        """
 
160
 
 
161
        path = relpath
 
162
        try:
 
163
            path = self._abspath(relpath)
 
164
            check_legal_path(path)
 
165
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
 
166
        except (IOError, OSError),e:
 
167
            self._translate_error(e, path)
 
168
        try:
 
169
            fp.write(bytes)
 
170
            fp.commit()
 
171
        finally:
 
172
            fp.close()
 
173
 
 
174
    def iter_files_recursive(self):
 
175
        """Iter the relative paths of files in the transports sub-tree."""
 
176
        queue = list(self.list_dir(u'.'))
 
177
        while queue:
 
178
            relpath = queue.pop(0)
 
179
            st = self.stat(relpath)
 
180
            if S_ISDIR(st[ST_MODE]):
 
181
                for i, basename in enumerate(self.list_dir(relpath)):
 
182
                    queue.insert(i, relpath+'/'+basename)
 
183
            else:
 
184
                yield relpath
 
185
 
 
186
    def mkdir(self, relpath, mode=None):
 
187
        """Create a directory at the given path."""
 
188
        path = relpath
 
189
        try:
 
190
            if mode is None:
 
191
                # os.mkdir() will filter through umask
 
192
                local_mode = 0777
 
193
            else:
 
194
                local_mode = mode
 
195
            path = self._abspath(relpath)
 
196
            os.mkdir(path, local_mode)
 
197
            if mode is not None:
 
198
                # It is probably faster to just do the chmod, rather than
 
199
                # doing a stat, and then trying to compare
 
200
                os.chmod(path, mode)
 
201
        except (IOError, OSError),e:
 
202
            self._translate_error(e, path)
 
203
 
 
204
    def append(self, relpath, f, mode=None):
 
205
        """Append the text in the file-like object into the final location."""
 
206
        abspath = self._abspath(relpath)
 
207
        if mode is None:
 
208
            # os.open() will automatically use the umask
 
209
            local_mode = 0666
 
210
        else:
 
211
            local_mode = mode
 
212
        try:
 
213
            fd = os.open(abspath, _append_flags, local_mode)
 
214
        except (IOError, OSError),e:
 
215
            self._translate_error(e, relpath)
 
216
        try:
 
217
            st = os.fstat(fd)
 
218
            result = st.st_size
 
219
            if mode is not None and mode != S_IMODE(st.st_mode):
 
220
                # Because of umask, we may still need to chmod the file.
 
221
                # But in the general case, we won't have to
 
222
                os.chmod(abspath, mode)
 
223
            self._pump_to_fd(f, fd)
 
224
        finally:
 
225
            os.close(fd)
 
226
        return result
 
227
 
 
228
    def _pump_to_fd(self, fromfile, to_fd):
 
229
        """Copy contents of one file to another."""
 
230
        BUFSIZE = 32768
 
231
        while True:
 
232
            b = fromfile.read(BUFSIZE)
 
233
            if not b:
 
234
                break
 
235
            os.write(to_fd, b)
 
236
 
 
237
    def copy(self, rel_from, rel_to):
 
238
        """Copy the item at rel_from to the location at rel_to"""
 
239
        path_from = self._abspath(rel_from)
 
240
        path_to = self._abspath(rel_to)
 
241
        try:
 
242
            shutil.copy(path_from, path_to)
 
243
        except (IOError, OSError),e:
 
244
            # TODO: What about path_to?
 
245
            self._translate_error(e, path_from)
 
246
 
 
247
    def rename(self, rel_from, rel_to):
 
248
        path_from = self._abspath(rel_from)
 
249
        try:
 
250
            # *don't* call bzrlib.osutils.rename, because we want to 
 
251
            # detect errors on rename
 
252
            os.rename(path_from, self._abspath(rel_to))
 
253
        except (IOError, OSError),e:
 
254
            # TODO: What about path_to?
 
255
            self._translate_error(e, path_from)
 
256
 
 
257
    def move(self, rel_from, rel_to):
 
258
        """Move the item at rel_from to the location at rel_to"""
 
259
        path_from = self._abspath(rel_from)
 
260
        path_to = self._abspath(rel_to)
 
261
 
 
262
        try:
 
263
            # this version will delete the destination if necessary
 
264
            rename(path_from, path_to)
 
265
        except (IOError, OSError),e:
 
266
            # TODO: What about path_to?
 
267
            self._translate_error(e, path_from)
 
268
 
 
269
    def delete(self, relpath):
 
270
        """Delete the item at relpath"""
 
271
        path = relpath
 
272
        try:
 
273
            path = self._abspath(relpath)
 
274
            os.remove(path)
 
275
        except (IOError, OSError),e:
 
276
            self._translate_error(e, path)
 
277
 
 
278
    def copy_to(self, relpaths, other, mode=None, pb=None):
 
279
        """Copy a set of entries from self into another Transport.
 
280
 
 
281
        :param relpaths: A list/generator of entries to be copied.
 
282
        """
 
283
        if isinstance(other, LocalTransport):
 
284
            # Both from & to are on the local filesystem
 
285
            # Unfortunately, I can't think of anything faster than just
 
286
            # copying them across, one by one :(
 
287
            total = self._get_total(relpaths)
 
288
            count = 0
 
289
            for path in relpaths:
 
290
                self._update_pb(pb, 'copy-to', count, total)
 
291
                try:
 
292
                    mypath = self._abspath(path)
 
293
                    otherpath = other._abspath(path)
 
294
                    shutil.copy(mypath, otherpath)
 
295
                    if mode is not None:
 
296
                        os.chmod(otherpath, mode)
 
297
                except (IOError, OSError),e:
 
298
                    self._translate_error(e, path)
 
299
                count += 1
 
300
            return count
 
301
        else:
 
302
            return super(LocalTransport, self).copy_to(relpaths, other, mode=mode, pb=pb)
 
303
 
 
304
    def listable(self):
 
305
        """See Transport.listable."""
 
306
        return True
 
307
 
 
308
    def list_dir(self, relpath):
 
309
        """Return a list of all files at the given location.
 
310
        WARNING: many transports do not support this, so trying avoid using
 
311
        it if at all possible.
 
312
        """
 
313
        path = self._abspath(relpath)
 
314
        try:
 
315
            entries = os.listdir(path)
 
316
        except (IOError, OSError), e:
 
317
            self._translate_error(e, path)
 
318
        return [urlutils.escape(entry) for entry in entries]
 
319
 
 
320
    def stat(self, relpath):
 
321
        """Return the stat information for a file.
 
322
        """
 
323
        path = relpath
 
324
        try:
 
325
            path = self._abspath(relpath)
 
326
            return os.stat(path)
 
327
        except (IOError, OSError),e:
 
328
            self._translate_error(e, path)
 
329
 
 
330
    def lock_read(self, relpath):
 
331
        """Lock the given file for shared (read) access.
 
332
        :return: A lock object, which should be passed to Transport.unlock()
 
333
        """
 
334
        from bzrlib.lock import ReadLock
 
335
        path = relpath
 
336
        try:
 
337
            path = self._abspath(relpath)
 
338
            return ReadLock(path)
 
339
        except (IOError, OSError), e:
 
340
            self._translate_error(e, path)
 
341
 
 
342
    def lock_write(self, relpath):
 
343
        """Lock the given file for exclusive (write) access.
 
344
        WARNING: many transports do not support this, so trying avoid using it
 
345
 
 
346
        :return: A lock object, which should be passed to Transport.unlock()
 
347
        """
 
348
        from bzrlib.lock import WriteLock
 
349
        return WriteLock(self._abspath(relpath))
 
350
 
 
351
    def rmdir(self, relpath):
 
352
        """See Transport.rmdir."""
 
353
        path = relpath
 
354
        try:
 
355
            path = self._abspath(relpath)
 
356
            os.rmdir(path)
 
357
        except (IOError, OSError),e:
 
358
            self._translate_error(e, path)
 
359
 
 
360
    def _can_roundtrip_unix_modebits(self):
 
361
        if sys.platform == 'win32':
 
362
            # anyone else?
 
363
            return False
 
364
        else:
 
365
            return True
 
366
 
 
367
 
 
368
class LocalRelpathServer(Server):
 
369
    """A pretend server for local transports, using relpaths."""
 
370
 
 
371
    def get_url(self):
 
372
        """See Transport.Server.get_url."""
 
373
        return "."
 
374
 
 
375
 
 
376
class LocalAbspathServer(Server):
 
377
    """A pretend server for local transports, using absolute paths."""
 
378
 
 
379
    def get_url(self):
 
380
        """See Transport.Server.get_url."""
 
381
        return os.path.abspath("")
 
382
 
 
383
 
 
384
class LocalURLServer(Server):
 
385
    """A pretend server for local transports, using file:// urls."""
 
386
 
 
387
    def get_url(self):
 
388
        """See Transport.Server.get_url."""
 
389
        return urlutils.local_path_to_url('')
 
390
 
 
391
 
 
392
def get_test_permutations():
 
393
    """Return the permutations to be used in testing."""
 
394
    return [(LocalTransport, LocalRelpathServer),
 
395
            (LocalTransport, LocalAbspathServer),
 
396
            (LocalTransport, LocalURLServer),
 
397
            ]