~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-02 17:16:12 UTC
  • mto: This revision was merged to the branch mainline in revision 5369.
  • Revision ID: john@arbash-meinel.com-20100802171612-rdh5ods70w2bl3j7
We also have to re-implement it for _simple_set_pyx.pyx

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
 
33
33
    osutils,
34
34
    urlutils,
35
35
    symbol_versioning,
 
36
    transport,
36
37
    )
37
38
from bzrlib.trace import mutter
 
39
from bzrlib.transport import LateReadError
38
40
""")
39
41
 
40
 
from bzrlib.transport import Transport, Server
41
 
 
42
 
 
43
 
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
44
 
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
45
 
 
46
 
 
47
 
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):
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 + '/'
 
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
 
64
75
        super(LocalTransport, self).__init__(base)
65
76
        self._local_base = urlutils.local_path_from_url(base)
66
77
 
67
 
    def should_cache(self):
68
 
        return False
69
 
 
70
78
    def clone(self, offset=None):
71
79
        """Return a new LocalTransport with root at self.base + offset
72
 
        Because the local filesystem does not require a connection, 
 
80
        Because the local filesystem does not require a connection,
73
81
        we can just return a new object.
74
82
        """
75
83
        if offset is None:
91
99
         - relative_reference is url escaped.
92
100
        """
93
101
        if relative_reference in ('.', ''):
94
 
            return self._local_base
 
102
            # _local_base normally has a trailing slash; strip it so that stat
 
103
            # on a transport pointing to a symlink reads the link not the
 
104
            # referent but be careful of / and c:\
 
105
            return osutils.split(self._local_base)[0]
95
106
        return self._local_base + urlutils.unescape(relative_reference)
96
107
 
97
108
    def abspath(self, relpath):
98
109
        """Return the full url to the given relative URL."""
99
110
        # TODO: url escape the result. RBC 20060523.
100
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
101
111
        # jam 20060426 Using normpath on the real path, because that ensures
102
112
        #       proper handling of stuff like
103
113
        path = osutils.normpath(osutils.pathjoin(
104
114
                    self._local_base, urlutils.unescape(relpath)))
 
115
        # on windows, our _local_base may or may not have a drive specified
 
116
        # (ie, it may be "/" or "c:/foo").
 
117
        # If 'relpath' is '/' we *always* get back an abspath without
 
118
        # the drive letter - but if our transport already has a drive letter,
 
119
        # we want our abspaths to have a drive letter too - so handle that
 
120
        # here.
 
121
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
 
122
            and path == '/'):
 
123
            path = self._local_base[:3]
 
124
 
105
125
        return urlutils.local_path_to_url(path)
106
126
 
107
127
    def local_abspath(self, relpath):
110
130
        This function only exists for the LocalTransport, since it is
111
131
        the only one that has direct local access.
112
132
        This is mostly for stuff like WorkingTree which needs to know
113
 
        the local working directory.
114
 
        
 
133
        the local working directory.  The returned path will always contain
 
134
        forward slashes as the path separator, regardless of the platform.
 
135
 
115
136
        This function is quite expensive: it calls realpath which resolves
116
137
        symlinks.
117
138
        """
126
147
            abspath = u'.'
127
148
 
128
149
        return urlutils.file_relpath(
129
 
            urlutils.strip_trailing_slash(self.base), 
 
150
            urlutils.strip_trailing_slash(self.base),
130
151
            urlutils.strip_trailing_slash(abspath))
131
152
 
132
153
    def has(self, relpath):
137
158
 
138
159
        :param relpath: The relative path to the file
139
160
        """
 
161
        canonical_url = self.abspath(relpath)
 
162
        if canonical_url in transport._file_streams:
 
163
            transport._file_streams[canonical_url].flush()
140
164
        try:
141
165
            path = self._abspath(relpath)
142
 
            return open(path, 'rb')
 
166
            return osutils.open_file(path, 'rb')
143
167
        except (IOError, OSError),e:
 
168
            if e.errno == errno.EISDIR:
 
169
                return LateReadError(relpath)
144
170
            self._translate_error(e, path)
145
171
 
146
172
    def put_file(self, relpath, f, mode=None):
148
174
 
149
175
        :param relpath: Location to put the contents, relative to base.
150
176
        :param f:       File-like object.
151
 
        :param mode: The mode for the newly created file, 
 
177
        :param mode: The mode for the newly created file,
152
178
                     None means just use the default
153
179
        """
154
180
 
160
186
        except (IOError, OSError),e:
161
187
            self._translate_error(e, path)
162
188
        try:
163
 
            self._pump(f, fp)
 
189
            length = self._pump(f, fp)
164
190
            fp.commit()
165
191
        finally:
166
192
            fp.close()
 
193
        return length
167
194
 
168
195
    def put_bytes(self, relpath, bytes, mode=None):
169
196
        """Copy the string into the location.
180
207
        except (IOError, OSError),e:
181
208
            self._translate_error(e, path)
182
209
        try:
183
 
            fp.write(bytes)
 
210
            if bytes:
 
211
                fp.write(bytes)
184
212
            fp.commit()
185
213
        finally:
186
214
            fp.close()
261
289
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
262
290
                             create_parent_dir=False, dir_mode=None):
263
291
        def writer(fd):
264
 
            os.write(fd, bytes)
 
292
            if bytes:
 
293
                os.write(fd, bytes)
265
294
        self._put_non_atomic_helper(relpath, writer, mode=mode,
266
295
                                    create_parent_dir=create_parent_dir,
267
296
                                    dir_mode=dir_mode)
298
327
        """Create a directory at the given path."""
299
328
        self._mkdir(self._abspath(relpath), mode=mode)
300
329
 
 
330
    def open_write_stream(self, relpath, mode=None):
 
331
        """See Transport.open_write_stream."""
 
332
        # initialise the file
 
333
        self.put_bytes_non_atomic(relpath, "", mode=mode)
 
334
        abspath = self._abspath(relpath)
 
335
        handle = osutils.open_file(abspath, 'wb')
 
336
        if mode is not None:
 
337
            self._check_mode_and_size(abspath, handle.fileno(), mode)
 
338
        transport._file_streams[self.abspath(relpath)] = handle
 
339
        return transport.FileFileStream(self, relpath, handle)
 
340
 
301
341
    def _get_append_file(self, relpath, mode=None):
302
342
        """Call os.open() for the given relpath"""
303
343
        file_abspath = self._abspath(relpath)
335
375
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
336
376
        try:
337
377
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
338
 
            os.write(fd, bytes)
 
378
            if bytes:
 
379
                os.write(fd, bytes)
339
380
        finally:
340
381
            os.close(fd)
341
382
        return result
361
402
 
362
403
    def rename(self, rel_from, rel_to):
363
404
        path_from = self._abspath(rel_from)
 
405
        path_to = self._abspath(rel_to)
364
406
        try:
365
 
            # *don't* call bzrlib.osutils.rename, because we want to 
366
 
            # detect errors on rename
367
 
            os.rename(path_from, self._abspath(rel_to))
 
407
            # *don't* call bzrlib.osutils.rename, because we want to
 
408
            # detect conflicting names on rename, and osutils.rename tries to
 
409
            # mask cross-platform differences there
 
410
            os.rename(path_from, path_to)
368
411
        except (IOError, OSError),e:
369
412
            # TODO: What about path_to?
370
413
            self._translate_error(e, path_from)
390
433
        except (IOError, OSError),e:
391
434
            self._translate_error(e, path)
392
435
 
 
436
    def external_url(self):
 
437
        """See bzrlib.transport.Transport.external_url."""
 
438
        # File URL's are externally usable.
 
439
        return self.base
 
440
 
393
441
    def copy_to(self, relpaths, other, mode=None, pb=None):
394
442
        """Copy a set of entries from self into another Transport.
395
443
 
438
486
        path = relpath
439
487
        try:
440
488
            path = self._abspath(relpath)
441
 
            return os.stat(path)
 
489
            return os.lstat(path)
442
490
        except (IOError, OSError),e:
443
491
            self._translate_error(e, path)
444
492
 
472
520
        except (IOError, OSError),e:
473
521
            self._translate_error(e, path)
474
522
 
 
523
    if osutils.host_os_dereferences_symlinks():
 
524
        def readlink(self, relpath):
 
525
            """See Transport.readlink."""
 
526
            return osutils.readlink(self._abspath(relpath))
 
527
 
 
528
    if osutils.hardlinks_good():
 
529
        def hardlink(self, source, link_name):
 
530
            """See Transport.link."""
 
531
            try:
 
532
                os.link(self._abspath(source), self._abspath(link_name))
 
533
            except (IOError, OSError), e:
 
534
                self._translate_error(e, source)
 
535
 
 
536
    if osutils.has_symlinks():
 
537
        def symlink(self, source, link_name):
 
538
            """See Transport.symlink."""
 
539
            abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
 
540
            source_rel = urlutils.file_relpath(
 
541
                urlutils.strip_trailing_slash(abs_link_dirpath),
 
542
                urlutils.strip_trailing_slash(self.abspath(source))
 
543
            )
 
544
 
 
545
            try:
 
546
                os.symlink(source_rel, self._abspath(link_name))
 
547
            except (IOError, OSError), e:
 
548
                self._translate_error(e, source_rel)
 
549
 
475
550
    def _can_roundtrip_unix_modebits(self):
476
551
        if sys.platform == 'win32':
477
552
            # anyone else?
490
565
        self._local_base = urlutils._win32_local_path_from_url(base)
491
566
 
492
567
    def abspath(self, relpath):
493
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
494
568
        path = osutils.normpath(osutils.pathjoin(
495
569
                    self._local_base, urlutils.unescape(relpath)))
496
570
        return urlutils._win32_local_path_to_url(path)
497
571
 
498
572
    def clone(self, offset=None):
499
573
        """Return a new LocalTransport with root at self.base + offset
500
 
        Because the local filesystem does not require a connection, 
 
574
        Because the local filesystem does not require a connection,
501
575
        we can just return a new object.
502
576
        """
503
577
        if offset is None:
512
586
            return EmulatedWin32LocalTransport(abspath)
513
587
 
514
588
 
515
 
class LocalURLServer(Server):
516
 
    """A pretend server for local transports, using file:// urls.
517
 
    
518
 
    Of course no actual server is required to access the local filesystem, so
519
 
    this just exists to tell the test code how to get to it.
520
 
    """
521
 
 
522
 
    def setUp(self):
523
 
        """Setup the server to service requests.
524
 
        
525
 
        :param decorated_transport: ignored by this implementation.
526
 
        """
527
 
 
528
 
    def get_url(self):
529
 
        """See Transport.Server.get_url."""
530
 
        return urlutils.local_path_to_url('')
531
 
 
532
 
 
533
589
def get_test_permutations():
534
590
    """Return the permutations to be used in testing."""
535
 
    return [
536
 
            (LocalTransport, LocalURLServer),
537
 
            ]
 
591
    from bzrlib.tests import test_server
 
592
    return [(LocalTransport, test_server.LocalURLServer),]