~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Patch Queue Manager
  • Date: 2012-09-17 11:14:25 UTC
  • mfrom: (6554.1.1 stack-remove-unknown)
  • Revision ID: pqm@pqm.ubuntu.com-20120917111425-4i6r0ze0v9zm33bu
(vila) Fix obscure and misleading warning when trying to delete an unknown
 config option. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Transport for the local filesystem.
18
18
 
19
19
This is a fairly thin wrapper on regular file IO.
20
20
"""
21
21
 
 
22
from __future__ import absolute_import
 
23
 
22
24
import os
23
 
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
 
25
from stat import ST_MODE, S_ISDIR, S_IMODE
24
26
import sys
25
27
 
26
28
from bzrlib.lazy_import import lazy_import
34
36
    urlutils,
35
37
    symbol_versioning,
36
38
    )
37
 
from bzrlib.trace import mutter
38
39
from bzrlib.transport import LateReadError
39
40
""")
40
41
 
41
 
from bzrlib.transport import Transport, Server
42
 
 
43
 
 
44
 
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
45
 
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
46
 
 
47
 
 
48
 
class LocalTransport(Transport):
 
42
from bzrlib import transport
 
43
 
 
44
 
 
45
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
 
46
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
 
47
 
 
48
 
 
49
class LocalTransport(transport.Transport):
49
50
    """This is the transport agent for local filesystem access."""
50
51
 
51
52
    def __init__(self, base):
52
53
        """Set the base path where files will be stored."""
53
54
        if not base.startswith('file://'):
54
 
            symbol_versioning.warn(
55
 
                "Instantiating LocalTransport with a filesystem path"
56
 
                " is deprecated as of bzr 0.8."
57
 
                " Please use bzrlib.transport.get_transport()"
58
 
                " or pass in a file:// url.",
59
 
                 DeprecationWarning,
60
 
                 stacklevel=2
61
 
                 )
62
 
            base = urlutils.local_path_to_url(base)
 
55
            raise AssertionError("not a file:// url: %r" % base)
63
56
        if base[-1] != '/':
64
57
            base = base + '/'
 
58
 
 
59
        # Special case : windows has no "root", but does have
 
60
        # multiple lettered drives inside it. #240910
 
61
        if sys.platform == 'win32' and base == 'file:///':
 
62
            base = ''
 
63
            self._local_base = ''
 
64
            super(LocalTransport, self).__init__(base)
 
65
            return
 
66
 
65
67
        super(LocalTransport, self).__init__(base)
66
68
        self._local_base = urlutils.local_path_from_url(base)
67
 
 
68
 
    def should_cache(self):
69
 
        return False
 
69
        if self._local_base[-1] != '/':
 
70
            self._local_base = self._local_base + '/'
70
71
 
71
72
    def clone(self, offset=None):
72
73
        """Return a new LocalTransport with root at self.base + offset
73
 
        Because the local filesystem does not require a connection, 
 
74
        Because the local filesystem does not require a connection,
74
75
        we can just return a new object.
75
76
        """
76
77
        if offset is None:
92
93
         - relative_reference is url escaped.
93
94
        """
94
95
        if relative_reference in ('.', ''):
95
 
            return self._local_base
 
96
            # _local_base normally has a trailing slash; strip it so that stat
 
97
            # on a transport pointing to a symlink reads the link not the
 
98
            # referent but be careful of / and c:\
 
99
            return osutils.split(self._local_base)[0]
96
100
        return self._local_base + urlutils.unescape(relative_reference)
97
101
 
98
102
    def abspath(self, relpath):
99
103
        """Return the full url to the given relative URL."""
100
104
        # TODO: url escape the result. RBC 20060523.
101
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
102
105
        # jam 20060426 Using normpath on the real path, because that ensures
103
106
        #       proper handling of stuff like
104
107
        path = osutils.normpath(osutils.pathjoin(
105
108
                    self._local_base, urlutils.unescape(relpath)))
 
109
        # on windows, our _local_base may or may not have a drive specified
 
110
        # (ie, it may be "/" or "c:/foo").
 
111
        # If 'relpath' is '/' we *always* get back an abspath without
 
112
        # the drive letter - but if our transport already has a drive letter,
 
113
        # we want our abspaths to have a drive letter too - so handle that
 
114
        # here.
 
115
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
 
116
            and path == '/'):
 
117
            path = self._local_base[:3]
 
118
 
106
119
        return urlutils.local_path_to_url(path)
107
120
 
108
121
    def local_abspath(self, relpath):
111
124
        This function only exists for the LocalTransport, since it is
112
125
        the only one that has direct local access.
113
126
        This is mostly for stuff like WorkingTree which needs to know
114
 
        the local working directory.
115
 
        
 
127
        the local working directory.  The returned path will always contain
 
128
        forward slashes as the path separator, regardless of the platform.
 
129
 
116
130
        This function is quite expensive: it calls realpath which resolves
117
131
        symlinks.
118
132
        """
126
140
        if abspath is None:
127
141
            abspath = u'.'
128
142
 
129
 
        return urlutils.file_relpath(
130
 
            urlutils.strip_trailing_slash(self.base),
131
 
            urlutils.strip_trailing_slash(abspath))
 
143
        return urlutils.file_relpath(self.base, abspath)
132
144
 
133
145
    def has(self, relpath):
134
146
        return os.access(self._abspath(relpath), os.F_OK)
138
150
 
139
151
        :param relpath: The relative path to the file
140
152
        """
 
153
        canonical_url = self.abspath(relpath)
 
154
        if canonical_url in transport._file_streams:
 
155
            transport._file_streams[canonical_url].flush()
141
156
        try:
142
157
            path = self._abspath(relpath)
143
 
            return open(path, 'rb')
 
158
            return osutils.open_file(path, 'rb')
144
159
        except (IOError, OSError),e:
145
160
            if e.errno == errno.EISDIR:
146
161
                return LateReadError(relpath)
151
166
 
152
167
        :param relpath: Location to put the contents, relative to base.
153
168
        :param f:       File-like object.
154
 
        :param mode: The mode for the newly created file, 
 
169
        :param mode: The mode for the newly created file,
155
170
                     None means just use the default
156
171
        """
157
172
 
163
178
        except (IOError, OSError),e:
164
179
            self._translate_error(e, path)
165
180
        try:
166
 
            self._pump(f, fp)
 
181
            length = self._pump(f, fp)
167
182
            fp.commit()
168
183
        finally:
169
184
            fp.close()
 
185
        return length
170
186
 
171
187
    def put_bytes(self, relpath, bytes, mode=None):
172
188
        """Copy the string into the location.
183
199
        except (IOError, OSError),e:
184
200
            self._translate_error(e, path)
185
201
        try:
186
 
            fp.write(bytes)
 
202
            if bytes:
 
203
                fp.write(bytes)
187
204
            fp.commit()
188
205
        finally:
189
206
            fp.close()
232
249
            if mode is not None and mode != S_IMODE(st.st_mode):
233
250
                # Because of umask, we may still need to chmod the file.
234
251
                # But in the general case, we won't have to
235
 
                os.chmod(abspath, mode)
 
252
                osutils.chmod_if_possible(abspath, mode)
236
253
            writer(fd)
237
254
        finally:
238
255
            os.close(fd)
264
281
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
265
282
                             create_parent_dir=False, dir_mode=None):
266
283
        def writer(fd):
267
 
            os.write(fd, bytes)
 
284
            if bytes:
 
285
                os.write(fd, bytes)
268
286
        self._put_non_atomic_helper(relpath, writer, mode=mode,
269
287
                                    create_parent_dir=create_parent_dir,
270
288
                                    dir_mode=dir_mode)
290
308
            local_mode = mode
291
309
        try:
292
310
            os.mkdir(abspath, local_mode)
293
 
            if mode is not None:
294
 
                # It is probably faster to just do the chmod, rather than
295
 
                # doing a stat, and then trying to compare
296
 
                os.chmod(abspath, mode)
297
311
        except (IOError, OSError),e:
298
312
            self._translate_error(e, abspath)
 
313
        if mode is not None:
 
314
            try:
 
315
                osutils.chmod_if_possible(abspath, mode)
 
316
            except (IOError, OSError), e:
 
317
                self._translate_error(e, abspath)
299
318
 
300
319
    def mkdir(self, relpath, mode=None):
301
320
        """Create a directory at the given path."""
302
321
        self._mkdir(self._abspath(relpath), mode=mode)
303
322
 
 
323
    def open_write_stream(self, relpath, mode=None):
 
324
        """See Transport.open_write_stream."""
 
325
        abspath = self._abspath(relpath)
 
326
        try:
 
327
            handle = osutils.open_file(abspath, 'wb')
 
328
        except (IOError, OSError),e:
 
329
            self._translate_error(e, abspath)
 
330
        handle.truncate()
 
331
        if mode is not None:
 
332
            self._check_mode_and_size(abspath, handle.fileno(), mode)
 
333
        transport._file_streams[self.abspath(relpath)] = handle
 
334
        return transport.FileFileStream(self, relpath, handle)
 
335
 
304
336
    def _get_append_file(self, relpath, mode=None):
305
337
        """Call os.open() for the given relpath"""
306
338
        file_abspath = self._abspath(relpath)
320
352
        if mode is not None and mode != S_IMODE(st.st_mode):
321
353
            # Because of umask, we may still need to chmod the file.
322
354
            # But in the general case, we won't have to
323
 
            os.chmod(file_abspath, mode)
 
355
            osutils.chmod_if_possible(file_abspath, mode)
324
356
        return st.st_size
325
357
 
326
358
    def append_file(self, relpath, f, mode=None):
338
370
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
339
371
        try:
340
372
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
341
 
            os.write(fd, bytes)
 
373
            if bytes:
 
374
                os.write(fd, bytes)
342
375
        finally:
343
376
            os.close(fd)
344
377
        return result
364
397
 
365
398
    def rename(self, rel_from, rel_to):
366
399
        path_from = self._abspath(rel_from)
 
400
        path_to = self._abspath(rel_to)
367
401
        try:
368
 
            # *don't* call bzrlib.osutils.rename, because we want to 
369
 
            # detect errors on rename
370
 
            os.rename(path_from, self._abspath(rel_to))
 
402
            # *don't* call bzrlib.osutils.rename, because we want to
 
403
            # detect conflicting names on rename, and osutils.rename tries to
 
404
            # mask cross-platform differences there
 
405
            os.rename(path_from, path_to)
371
406
        except (IOError, OSError),e:
372
407
            # TODO: What about path_to?
373
408
            self._translate_error(e, path_from)
416
451
                    otherpath = other._abspath(path)
417
452
                    shutil.copy(mypath, otherpath)
418
453
                    if mode is not None:
419
 
                        os.chmod(otherpath, mode)
 
454
                        osutils.chmod_if_possible(otherpath, mode)
420
455
                except (IOError, OSError),e:
421
456
                    self._translate_error(e, path)
422
457
                count += 1
446
481
        path = relpath
447
482
        try:
448
483
            path = self._abspath(relpath)
449
 
            return os.stat(path)
 
484
            return os.lstat(path)
450
485
        except (IOError, OSError),e:
451
486
            self._translate_error(e, path)
452
487
 
480
515
        except (IOError, OSError),e:
481
516
            self._translate_error(e, path)
482
517
 
 
518
    if osutils.host_os_dereferences_symlinks():
 
519
        def readlink(self, relpath):
 
520
            """See Transport.readlink."""
 
521
            return osutils.readlink(self._abspath(relpath))
 
522
 
 
523
    if osutils.hardlinks_good():
 
524
        def hardlink(self, source, link_name):
 
525
            """See Transport.link."""
 
526
            try:
 
527
                os.link(self._abspath(source), self._abspath(link_name))
 
528
            except (IOError, OSError), e:
 
529
                self._translate_error(e, source)
 
530
 
 
531
    if osutils.has_symlinks():
 
532
        def symlink(self, source, link_name):
 
533
            """See Transport.symlink."""
 
534
            abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
 
535
            source_rel = urlutils.file_relpath(
 
536
                abs_link_dirpath, self.abspath(source))
 
537
 
 
538
            try:
 
539
                os.symlink(source_rel, self._abspath(link_name))
 
540
            except (IOError, OSError), e:
 
541
                self._translate_error(e, source_rel)
 
542
 
483
543
    def _can_roundtrip_unix_modebits(self):
484
544
        if sys.platform == 'win32':
485
545
            # anyone else?
498
558
        self._local_base = urlutils._win32_local_path_from_url(base)
499
559
 
500
560
    def abspath(self, relpath):
501
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
502
 
        path = osutils.normpath(osutils.pathjoin(
 
561
        path = osutils._win32_normpath(osutils.pathjoin(
503
562
                    self._local_base, urlutils.unescape(relpath)))
504
563
        return urlutils._win32_local_path_to_url(path)
505
564
 
506
565
    def clone(self, offset=None):
507
566
        """Return a new LocalTransport with root at self.base + offset
508
 
        Because the local filesystem does not require a connection, 
 
567
        Because the local filesystem does not require a connection,
509
568
        we can just return a new object.
510
569
        """
511
570
        if offset is None:
520
579
            return EmulatedWin32LocalTransport(abspath)
521
580
 
522
581
 
523
 
class LocalURLServer(Server):
524
 
    """A pretend server for local transports, using file:// urls.
525
 
    
526
 
    Of course no actual server is required to access the local filesystem, so
527
 
    this just exists to tell the test code how to get to it.
528
 
    """
529
 
 
530
 
    def setUp(self):
531
 
        """Setup the server to service requests.
532
 
        
533
 
        :param decorated_transport: ignored by this implementation.
534
 
        """
535
 
 
536
 
    def get_url(self):
537
 
        """See Transport.Server.get_url."""
538
 
        return urlutils.local_path_to_url('')
539
 
 
540
 
 
541
582
def get_test_permutations():
542
583
    """Return the permutations to be used in testing."""
543
 
    return [
544
 
            (LocalTransport, LocalURLServer),
545
 
            ]
 
584
    from bzrlib.tests import test_server
 
585
    return [(LocalTransport, test_server.LocalURLServer),]