~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-27 18:41:20 UTC
  • mto: This revision was merged to the branch mainline in revision 6449.
  • Revision ID: jelmer@samba.org-20120127184120-osvbbiwijy58hsah
remove unnecessary code.

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
33
35
    osutils,
34
36
    urlutils,
35
37
    symbol_versioning,
36
 
    transport,
37
38
    )
38
 
from bzrlib.trace import mutter
39
39
from bzrlib.transport import LateReadError
40
40
""")
41
41
 
42
 
from bzrlib.transport import Transport, Server
43
 
 
44
 
 
45
 
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
46
 
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
47
 
 
48
 
 
49
 
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):
50
50
    """This is the transport agent for local filesystem access."""
51
51
 
52
52
    def __init__(self, base):
63
63
            base = urlutils.local_path_to_url(base)
64
64
        if base[-1] != '/':
65
65
            base = base + '/'
 
66
 
 
67
        # Special case : windows has no "root", but does have
 
68
        # multiple lettered drives inside it. #240910
 
69
        if sys.platform == 'win32' and base == 'file:///':
 
70
            base = ''
 
71
            self._local_base = ''
 
72
            super(LocalTransport, self).__init__(base)
 
73
            return
 
74
 
66
75
        super(LocalTransport, self).__init__(base)
67
76
        self._local_base = urlutils.local_path_from_url(base)
 
77
        if self._local_base[-1] != '/':
 
78
            self._local_base = self._local_base + '/'
68
79
 
69
80
    def clone(self, offset=None):
70
81
        """Return a new LocalTransport with root at self.base + offset
71
 
        Because the local filesystem does not require a connection, 
 
82
        Because the local filesystem does not require a connection,
72
83
        we can just return a new object.
73
84
        """
74
85
        if offset is None:
90
101
         - relative_reference is url escaped.
91
102
        """
92
103
        if relative_reference in ('.', ''):
93
 
            return self._local_base
 
104
            # _local_base normally has a trailing slash; strip it so that stat
 
105
            # on a transport pointing to a symlink reads the link not the
 
106
            # referent but be careful of / and c:\
 
107
            return osutils.split(self._local_base)[0]
94
108
        return self._local_base + urlutils.unescape(relative_reference)
95
109
 
96
110
    def abspath(self, relpath):
97
111
        """Return the full url to the given relative URL."""
98
112
        # TODO: url escape the result. RBC 20060523.
99
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
100
113
        # jam 20060426 Using normpath on the real path, because that ensures
101
114
        #       proper handling of stuff like
102
115
        path = osutils.normpath(osutils.pathjoin(
103
116
                    self._local_base, urlutils.unescape(relpath)))
 
117
        # on windows, our _local_base may or may not have a drive specified
 
118
        # (ie, it may be "/" or "c:/foo").
 
119
        # If 'relpath' is '/' we *always* get back an abspath without
 
120
        # the drive letter - but if our transport already has a drive letter,
 
121
        # we want our abspaths to have a drive letter too - so handle that
 
122
        # here.
 
123
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
 
124
            and path == '/'):
 
125
            path = self._local_base[:3]
 
126
 
104
127
        return urlutils.local_path_to_url(path)
105
128
 
106
129
    def local_abspath(self, relpath):
109
132
        This function only exists for the LocalTransport, since it is
110
133
        the only one that has direct local access.
111
134
        This is mostly for stuff like WorkingTree which needs to know
112
 
        the local working directory.
113
 
        
 
135
        the local working directory.  The returned path will always contain
 
136
        forward slashes as the path separator, regardless of the platform.
 
137
 
114
138
        This function is quite expensive: it calls realpath which resolves
115
139
        symlinks.
116
140
        """
124
148
        if abspath is None:
125
149
            abspath = u'.'
126
150
 
127
 
        return urlutils.file_relpath(
128
 
            urlutils.strip_trailing_slash(self.base),
129
 
            urlutils.strip_trailing_slash(abspath))
 
151
        return urlutils.file_relpath(self.base, abspath)
130
152
 
131
153
    def has(self, relpath):
132
154
        return os.access(self._abspath(relpath), os.F_OK)
141
163
            transport._file_streams[canonical_url].flush()
142
164
        try:
143
165
            path = self._abspath(relpath)
144
 
            return open(path, 'rb')
 
166
            return osutils.open_file(path, 'rb')
145
167
        except (IOError, OSError),e:
146
168
            if e.errno == errno.EISDIR:
147
169
                return LateReadError(relpath)
152
174
 
153
175
        :param relpath: Location to put the contents, relative to base.
154
176
        :param f:       File-like object.
155
 
        :param mode: The mode for the newly created file, 
 
177
        :param mode: The mode for the newly created file,
156
178
                     None means just use the default
157
179
        """
158
180
 
185
207
        except (IOError, OSError),e:
186
208
            self._translate_error(e, path)
187
209
        try:
188
 
            fp.write(bytes)
 
210
            if bytes:
 
211
                fp.write(bytes)
189
212
            fp.commit()
190
213
        finally:
191
214
            fp.close()
234
257
            if mode is not None and mode != S_IMODE(st.st_mode):
235
258
                # Because of umask, we may still need to chmod the file.
236
259
                # But in the general case, we won't have to
237
 
                os.chmod(abspath, mode)
 
260
                osutils.chmod_if_possible(abspath, mode)
238
261
            writer(fd)
239
262
        finally:
240
263
            os.close(fd)
266
289
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
267
290
                             create_parent_dir=False, dir_mode=None):
268
291
        def writer(fd):
269
 
            os.write(fd, bytes)
 
292
            if bytes:
 
293
                os.write(fd, bytes)
270
294
        self._put_non_atomic_helper(relpath, writer, mode=mode,
271
295
                                    create_parent_dir=create_parent_dir,
272
296
                                    dir_mode=dir_mode)
292
316
            local_mode = mode
293
317
        try:
294
318
            os.mkdir(abspath, local_mode)
295
 
            if mode is not None:
296
 
                # It is probably faster to just do the chmod, rather than
297
 
                # doing a stat, and then trying to compare
298
 
                os.chmod(abspath, mode)
299
319
        except (IOError, OSError),e:
300
320
            self._translate_error(e, abspath)
 
321
        if mode is not None:
 
322
            try:
 
323
                osutils.chmod_if_possible(abspath, mode)
 
324
            except (IOError, OSError), e:
 
325
                self._translate_error(e, abspath)
301
326
 
302
327
    def mkdir(self, relpath, mode=None):
303
328
        """Create a directory at the given path."""
305
330
 
306
331
    def open_write_stream(self, relpath, mode=None):
307
332
        """See Transport.open_write_stream."""
308
 
        # initialise the file
309
 
        self.put_bytes_non_atomic(relpath, "", mode=mode)
310
333
        abspath = self._abspath(relpath)
311
 
        handle = open(abspath, 'wb')
 
334
        try:
 
335
            handle = osutils.open_file(abspath, 'wb')
 
336
        except (IOError, OSError),e:
 
337
            self._translate_error(e, abspath)
 
338
        handle.truncate()
312
339
        if mode is not None:
313
340
            self._check_mode_and_size(abspath, handle.fileno(), mode)
314
341
        transport._file_streams[self.abspath(relpath)] = handle
333
360
        if mode is not None and mode != S_IMODE(st.st_mode):
334
361
            # Because of umask, we may still need to chmod the file.
335
362
            # But in the general case, we won't have to
336
 
            os.chmod(file_abspath, mode)
 
363
            osutils.chmod_if_possible(file_abspath, mode)
337
364
        return st.st_size
338
365
 
339
366
    def append_file(self, relpath, f, mode=None):
351
378
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
352
379
        try:
353
380
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
354
 
            os.write(fd, bytes)
 
381
            if bytes:
 
382
                os.write(fd, bytes)
355
383
        finally:
356
384
            os.close(fd)
357
385
        return result
377
405
 
378
406
    def rename(self, rel_from, rel_to):
379
407
        path_from = self._abspath(rel_from)
 
408
        path_to = self._abspath(rel_to)
380
409
        try:
381
 
            # *don't* call bzrlib.osutils.rename, because we want to 
382
 
            # detect errors on rename
383
 
            os.rename(path_from, self._abspath(rel_to))
 
410
            # *don't* call bzrlib.osutils.rename, because we want to
 
411
            # detect conflicting names on rename, and osutils.rename tries to
 
412
            # mask cross-platform differences there
 
413
            os.rename(path_from, path_to)
384
414
        except (IOError, OSError),e:
385
415
            # TODO: What about path_to?
386
416
            self._translate_error(e, path_from)
429
459
                    otherpath = other._abspath(path)
430
460
                    shutil.copy(mypath, otherpath)
431
461
                    if mode is not None:
432
 
                        os.chmod(otherpath, mode)
 
462
                        osutils.chmod_if_possible(otherpath, mode)
433
463
                except (IOError, OSError),e:
434
464
                    self._translate_error(e, path)
435
465
                count += 1
459
489
        path = relpath
460
490
        try:
461
491
            path = self._abspath(relpath)
462
 
            return os.stat(path)
 
492
            return os.lstat(path)
463
493
        except (IOError, OSError),e:
464
494
            self._translate_error(e, path)
465
495
 
493
523
        except (IOError, OSError),e:
494
524
            self._translate_error(e, path)
495
525
 
 
526
    if osutils.host_os_dereferences_symlinks():
 
527
        def readlink(self, relpath):
 
528
            """See Transport.readlink."""
 
529
            return osutils.readlink(self._abspath(relpath))
 
530
 
 
531
    if osutils.hardlinks_good():
 
532
        def hardlink(self, source, link_name):
 
533
            """See Transport.link."""
 
534
            try:
 
535
                os.link(self._abspath(source), self._abspath(link_name))
 
536
            except (IOError, OSError), e:
 
537
                self._translate_error(e, source)
 
538
 
 
539
    if osutils.has_symlinks():
 
540
        def symlink(self, source, link_name):
 
541
            """See Transport.symlink."""
 
542
            abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
 
543
            source_rel = urlutils.file_relpath(
 
544
                abs_link_dirpath, self.abspath(source))
 
545
 
 
546
            try:
 
547
                os.symlink(source_rel, self._abspath(link_name))
 
548
            except (IOError, OSError), e:
 
549
                self._translate_error(e, source_rel)
 
550
 
496
551
    def _can_roundtrip_unix_modebits(self):
497
552
        if sys.platform == 'win32':
498
553
            # anyone else?
511
566
        self._local_base = urlutils._win32_local_path_from_url(base)
512
567
 
513
568
    def abspath(self, relpath):
514
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
515
 
        path = osutils.normpath(osutils.pathjoin(
 
569
        path = osutils._win32_normpath(osutils.pathjoin(
516
570
                    self._local_base, urlutils.unescape(relpath)))
517
571
        return urlutils._win32_local_path_to_url(path)
518
572
 
519
573
    def clone(self, offset=None):
520
574
        """Return a new LocalTransport with root at self.base + offset
521
 
        Because the local filesystem does not require a connection, 
 
575
        Because the local filesystem does not require a connection,
522
576
        we can just return a new object.
523
577
        """
524
578
        if offset is None:
533
587
            return EmulatedWin32LocalTransport(abspath)
534
588
 
535
589
 
536
 
class LocalURLServer(Server):
537
 
    """A pretend server for local transports, using file:// urls.
538
 
    
539
 
    Of course no actual server is required to access the local filesystem, so
540
 
    this just exists to tell the test code how to get to it.
541
 
    """
542
 
 
543
 
    def setUp(self):
544
 
        """Setup the server to service requests.
545
 
        
546
 
        :param decorated_transport: ignored by this implementation.
547
 
        """
548
 
 
549
 
    def get_url(self):
550
 
        """See Transport.Server.get_url."""
551
 
        return urlutils.local_path_to_url('')
552
 
 
553
 
 
554
590
def get_test_permutations():
555
591
    """Return the permutations to be used in testing."""
556
 
    return [
557
 
            (LocalTransport, LocalURLServer),
558
 
            ]
 
592
    from bzrlib.tests import test_server
 
593
    return [(LocalTransport, test_server.LocalURLServer),]