~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Ian Clatworthy
  • Date: 2007-12-11 02:07:30 UTC
  • mto: (3119.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3120.
  • Revision ID: ian.clatworthy@internode.on.net-20071211020730-sdj4kj794dw0628e
make help topics more discoverable

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
"""Transport for the local filesystem.
18
18
 
20
20
"""
21
21
 
22
22
import os
23
 
from stat import ST_MODE, S_ISDIR, S_IMODE
 
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
24
24
import sys
25
25
 
26
26
from bzrlib.lazy_import import lazy_import
33
33
    osutils,
34
34
    urlutils,
35
35
    symbol_versioning,
 
36
    transport,
36
37
    )
 
38
from bzrlib.trace import mutter
37
39
from bzrlib.transport import LateReadError
38
40
""")
39
41
 
40
 
from bzrlib import transport
41
 
 
42
 
 
43
 
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
44
 
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
45
 
 
46
 
 
47
 
class LocalTransport(transport.Transport):
 
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):
48
50
    """This is the transport agent for local filesystem access."""
49
51
 
50
52
    def __init__(self, base):
61
63
            base = urlutils.local_path_to_url(base)
62
64
        if base[-1] != '/':
63
65
            base = base + '/'
64
 
 
65
 
        # Special case : windows has no "root", but does have
66
 
        # multiple lettered drives inside it. #240910
67
 
        if sys.platform == 'win32' and base == 'file:///':
68
 
            base = ''
69
 
            self._local_base = ''
70
 
            super(LocalTransport, self).__init__(base)
71
 
            return
72
 
 
73
66
        super(LocalTransport, self).__init__(base)
74
67
        self._local_base = urlutils.local_path_from_url(base)
75
68
 
76
69
    def clone(self, offset=None):
77
70
        """Return a new LocalTransport with root at self.base + offset
78
 
        Because the local filesystem does not require a connection,
 
71
        Because the local filesystem does not require a connection, 
79
72
        we can just return a new object.
80
73
        """
81
74
        if offset is None:
97
90
         - relative_reference is url escaped.
98
91
        """
99
92
        if relative_reference in ('.', ''):
100
 
            # _local_base normally has a trailing slash; strip it so that stat
101
 
            # on a transport pointing to a symlink reads the link not the
102
 
            # referent but be careful of / and c:\
103
 
            return osutils.split(self._local_base)[0]
 
93
            return self._local_base
104
94
        return self._local_base + urlutils.unescape(relative_reference)
105
95
 
106
96
    def abspath(self, relpath):
107
97
        """Return the full url to the given relative URL."""
108
98
        # TODO: url escape the result. RBC 20060523.
 
99
        assert isinstance(relpath, basestring), (type(relpath), relpath)
109
100
        # jam 20060426 Using normpath on the real path, because that ensures
110
101
        #       proper handling of stuff like
111
102
        path = osutils.normpath(osutils.pathjoin(
112
103
                    self._local_base, urlutils.unescape(relpath)))
113
 
        # on windows, our _local_base may or may not have a drive specified
114
 
        # (ie, it may be "/" or "c:/foo").
115
 
        # If 'relpath' is '/' we *always* get back an abspath without
116
 
        # the drive letter - but if our transport already has a drive letter,
117
 
        # we want our abspaths to have a drive letter too - so handle that
118
 
        # here.
119
 
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
120
 
            and path == '/'):
121
 
            path = self._local_base[:3]
122
 
 
123
104
        return urlutils.local_path_to_url(path)
124
105
 
125
106
    def local_abspath(self, relpath):
128
109
        This function only exists for the LocalTransport, since it is
129
110
        the only one that has direct local access.
130
111
        This is mostly for stuff like WorkingTree which needs to know
131
 
        the local working directory.  The returned path will always contain
132
 
        forward slashes as the path separator, regardless of the platform.
133
 
 
 
112
        the local working directory.
 
113
        
134
114
        This function is quite expensive: it calls realpath which resolves
135
115
        symlinks.
136
116
        """
161
141
            transport._file_streams[canonical_url].flush()
162
142
        try:
163
143
            path = self._abspath(relpath)
164
 
            return osutils.open_file(path, 'rb')
 
144
            return open(path, 'rb')
165
145
        except (IOError, OSError),e:
166
146
            if e.errno == errno.EISDIR:
167
147
                return LateReadError(relpath)
172
152
 
173
153
        :param relpath: Location to put the contents, relative to base.
174
154
        :param f:       File-like object.
175
 
        :param mode: The mode for the newly created file,
 
155
        :param mode: The mode for the newly created file, 
176
156
                     None means just use the default
177
157
        """
178
158
 
205
185
        except (IOError, OSError),e:
206
186
            self._translate_error(e, path)
207
187
        try:
208
 
            if bytes:
209
 
                fp.write(bytes)
 
188
            fp.write(bytes)
210
189
            fp.commit()
211
190
        finally:
212
191
            fp.close()
287
266
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
288
267
                             create_parent_dir=False, dir_mode=None):
289
268
        def writer(fd):
290
 
            if bytes:
291
 
                os.write(fd, bytes)
 
269
            os.write(fd, bytes)
292
270
        self._put_non_atomic_helper(relpath, writer, mode=mode,
293
271
                                    create_parent_dir=create_parent_dir,
294
272
                                    dir_mode=dir_mode)
330
308
        # initialise the file
331
309
        self.put_bytes_non_atomic(relpath, "", mode=mode)
332
310
        abspath = self._abspath(relpath)
333
 
        handle = osutils.open_file(abspath, 'wb')
 
311
        handle = open(abspath, 'wb')
334
312
        if mode is not None:
335
313
            self._check_mode_and_size(abspath, handle.fileno(), mode)
336
314
        transport._file_streams[self.abspath(relpath)] = handle
373
351
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
374
352
        try:
375
353
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
376
 
            if bytes:
377
 
                os.write(fd, bytes)
 
354
            os.write(fd, bytes)
378
355
        finally:
379
356
            os.close(fd)
380
357
        return result
400
377
 
401
378
    def rename(self, rel_from, rel_to):
402
379
        path_from = self._abspath(rel_from)
403
 
        path_to = self._abspath(rel_to)
404
380
        try:
405
 
            # *don't* call bzrlib.osutils.rename, because we want to
406
 
            # detect conflicting names on rename, and osutils.rename tries to
407
 
            # mask cross-platform differences there
408
 
            os.rename(path_from, path_to)
 
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))
409
384
        except (IOError, OSError),e:
410
385
            # TODO: What about path_to?
411
386
            self._translate_error(e, path_from)
484
459
        path = relpath
485
460
        try:
486
461
            path = self._abspath(relpath)
487
 
            return os.lstat(path)
 
462
            return os.stat(path)
488
463
        except (IOError, OSError),e:
489
464
            self._translate_error(e, path)
490
465
 
518
493
        except (IOError, OSError),e:
519
494
            self._translate_error(e, path)
520
495
 
521
 
    if osutils.host_os_dereferences_symlinks():
522
 
        def readlink(self, relpath):
523
 
            """See Transport.readlink."""
524
 
            return osutils.readlink(self._abspath(relpath))
525
 
 
526
 
    if osutils.hardlinks_good():
527
 
        def hardlink(self, source, link_name):
528
 
            """See Transport.link."""
529
 
            try:
530
 
                os.link(self._abspath(source), self._abspath(link_name))
531
 
            except (IOError, OSError), e:
532
 
                self._translate_error(e, source)
533
 
 
534
 
    if osutils.has_symlinks():
535
 
        def symlink(self, source, link_name):
536
 
            """See Transport.symlink."""
537
 
            abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
538
 
            source_rel = urlutils.file_relpath(
539
 
                urlutils.strip_trailing_slash(abs_link_dirpath),
540
 
                urlutils.strip_trailing_slash(self.abspath(source))
541
 
            )
542
 
 
543
 
            try:
544
 
                os.symlink(source_rel, self._abspath(link_name))
545
 
            except (IOError, OSError), e:
546
 
                self._translate_error(e, source_rel)
547
 
 
548
496
    def _can_roundtrip_unix_modebits(self):
549
497
        if sys.platform == 'win32':
550
498
            # anyone else?
563
511
        self._local_base = urlutils._win32_local_path_from_url(base)
564
512
 
565
513
    def abspath(self, relpath):
 
514
        assert isinstance(relpath, basestring), (type(relpath), relpath)
566
515
        path = osutils.normpath(osutils.pathjoin(
567
516
                    self._local_base, urlutils.unescape(relpath)))
568
517
        return urlutils._win32_local_path_to_url(path)
569
518
 
570
519
    def clone(self, offset=None):
571
520
        """Return a new LocalTransport with root at self.base + offset
572
 
        Because the local filesystem does not require a connection,
 
521
        Because the local filesystem does not require a connection, 
573
522
        we can just return a new object.
574
523
        """
575
524
        if offset is None:
584
533
            return EmulatedWin32LocalTransport(abspath)
585
534
 
586
535
 
 
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
 
587
554
def get_test_permutations():
588
555
    """Return the permutations to be used in testing."""
589
 
    from bzrlib.tests import test_server
590
 
    return [(LocalTransport, test_server.LocalURLServer),]
 
556
    return [
 
557
            (LocalTransport, LocalURLServer),
 
558
            ]