~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

Merge bzr.dev and tree-file-ids-as-tuples.

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):
71
71
            self._local_base = ''
72
72
            super(LocalTransport, self).__init__(base)
73
73
            return
74
 
            
 
74
 
75
75
        super(LocalTransport, self).__init__(base)
76
76
        self._local_base = urlutils.local_path_from_url(base)
 
77
        if self._local_base[-1] != '/':
 
78
            self._local_base = self._local_base + '/'
77
79
 
78
80
    def clone(self, offset=None):
79
81
        """Return a new LocalTransport with root at self.base + offset
80
 
        Because the local filesystem does not require a connection, 
 
82
        Because the local filesystem does not require a connection,
81
83
        we can just return a new object.
82
84
        """
83
85
        if offset is None:
99
101
         - relative_reference is url escaped.
100
102
        """
101
103
        if relative_reference in ('.', ''):
102
 
            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]
103
108
        return self._local_base + urlutils.unescape(relative_reference)
104
109
 
105
110
    def abspath(self, relpath):
143
148
        if abspath is None:
144
149
            abspath = u'.'
145
150
 
146
 
        return urlutils.file_relpath(
147
 
            urlutils.strip_trailing_slash(self.base),
148
 
            urlutils.strip_trailing_slash(abspath))
 
151
        return urlutils.file_relpath(self.base, abspath)
149
152
 
150
153
    def has(self, relpath):
151
154
        return os.access(self._abspath(relpath), os.F_OK)
160
163
            transport._file_streams[canonical_url].flush()
161
164
        try:
162
165
            path = self._abspath(relpath)
163
 
            return open(path, 'rb')
 
166
            return osutils.open_file(path, 'rb')
164
167
        except (IOError, OSError),e:
165
168
            if e.errno == errno.EISDIR:
166
169
                return LateReadError(relpath)
171
174
 
172
175
        :param relpath: Location to put the contents, relative to base.
173
176
        :param f:       File-like object.
174
 
        :param mode: The mode for the newly created file, 
 
177
        :param mode: The mode for the newly created file,
175
178
                     None means just use the default
176
179
        """
177
180
 
204
207
        except (IOError, OSError),e:
205
208
            self._translate_error(e, path)
206
209
        try:
207
 
            fp.write(bytes)
 
210
            if bytes:
 
211
                fp.write(bytes)
208
212
            fp.commit()
209
213
        finally:
210
214
            fp.close()
253
257
            if mode is not None and mode != S_IMODE(st.st_mode):
254
258
                # Because of umask, we may still need to chmod the file.
255
259
                # But in the general case, we won't have to
256
 
                os.chmod(abspath, mode)
 
260
                osutils.chmod_if_possible(abspath, mode)
257
261
            writer(fd)
258
262
        finally:
259
263
            os.close(fd)
285
289
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
286
290
                             create_parent_dir=False, dir_mode=None):
287
291
        def writer(fd):
288
 
            os.write(fd, bytes)
 
292
            if bytes:
 
293
                os.write(fd, bytes)
289
294
        self._put_non_atomic_helper(relpath, writer, mode=mode,
290
295
                                    create_parent_dir=create_parent_dir,
291
296
                                    dir_mode=dir_mode)
311
316
            local_mode = mode
312
317
        try:
313
318
            os.mkdir(abspath, local_mode)
314
 
            if mode is not None:
315
 
                # It is probably faster to just do the chmod, rather than
316
 
                # doing a stat, and then trying to compare
317
 
                os.chmod(abspath, mode)
318
319
        except (IOError, OSError),e:
319
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)
320
326
 
321
327
    def mkdir(self, relpath, mode=None):
322
328
        """Create a directory at the given path."""
324
330
 
325
331
    def open_write_stream(self, relpath, mode=None):
326
332
        """See Transport.open_write_stream."""
327
 
        # initialise the file
328
 
        self.put_bytes_non_atomic(relpath, "", mode=mode)
329
333
        abspath = self._abspath(relpath)
330
 
        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()
331
339
        if mode is not None:
332
340
            self._check_mode_and_size(abspath, handle.fileno(), mode)
333
341
        transport._file_streams[self.abspath(relpath)] = handle
352
360
        if mode is not None and mode != S_IMODE(st.st_mode):
353
361
            # Because of umask, we may still need to chmod the file.
354
362
            # But in the general case, we won't have to
355
 
            os.chmod(file_abspath, mode)
 
363
            osutils.chmod_if_possible(file_abspath, mode)
356
364
        return st.st_size
357
365
 
358
366
    def append_file(self, relpath, f, mode=None):
370
378
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
371
379
        try:
372
380
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
373
 
            os.write(fd, bytes)
 
381
            if bytes:
 
382
                os.write(fd, bytes)
374
383
        finally:
375
384
            os.close(fd)
376
385
        return result
396
405
 
397
406
    def rename(self, rel_from, rel_to):
398
407
        path_from = self._abspath(rel_from)
 
408
        path_to = self._abspath(rel_to)
399
409
        try:
400
 
            # *don't* call bzrlib.osutils.rename, because we want to 
401
 
            # detect errors on rename
402
 
            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)
403
414
        except (IOError, OSError),e:
404
415
            # TODO: What about path_to?
405
416
            self._translate_error(e, path_from)
448
459
                    otherpath = other._abspath(path)
449
460
                    shutil.copy(mypath, otherpath)
450
461
                    if mode is not None:
451
 
                        os.chmod(otherpath, mode)
 
462
                        osutils.chmod_if_possible(otherpath, mode)
452
463
                except (IOError, OSError),e:
453
464
                    self._translate_error(e, path)
454
465
                count += 1
478
489
        path = relpath
479
490
        try:
480
491
            path = self._abspath(relpath)
481
 
            return os.stat(path)
 
492
            return os.lstat(path)
482
493
        except (IOError, OSError),e:
483
494
            self._translate_error(e, path)
484
495
 
512
523
        except (IOError, OSError),e:
513
524
            self._translate_error(e, path)
514
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
 
515
551
    def _can_roundtrip_unix_modebits(self):
516
552
        if sys.platform == 'win32':
517
553
            # anyone else?
530
566
        self._local_base = urlutils._win32_local_path_from_url(base)
531
567
 
532
568
    def abspath(self, relpath):
533
 
        path = osutils.normpath(osutils.pathjoin(
 
569
        path = osutils._win32_normpath(osutils.pathjoin(
534
570
                    self._local_base, urlutils.unescape(relpath)))
535
571
        return urlutils._win32_local_path_to_url(path)
536
572
 
537
573
    def clone(self, offset=None):
538
574
        """Return a new LocalTransport with root at self.base + offset
539
 
        Because the local filesystem does not require a connection, 
 
575
        Because the local filesystem does not require a connection,
540
576
        we can just return a new object.
541
577
        """
542
578
        if offset is None:
551
587
            return EmulatedWin32LocalTransport(abspath)
552
588
 
553
589
 
554
 
class LocalURLServer(Server):
555
 
    """A pretend server for local transports, using file:// urls.
556
 
    
557
 
    Of course no actual server is required to access the local filesystem, so
558
 
    this just exists to tell the test code how to get to it.
559
 
    """
560
 
 
561
 
    def setUp(self):
562
 
        """Setup the server to service requests.
563
 
        
564
 
        :param decorated_transport: ignored by this implementation.
565
 
        """
566
 
 
567
 
    def get_url(self):
568
 
        """See Transport.Server.get_url."""
569
 
        return urlutils.local_path_to_url('')
570
 
 
571
 
 
572
590
def get_test_permutations():
573
591
    """Return the permutations to be used in testing."""
574
 
    return [
575
 
            (LocalTransport, LocalURLServer),
576
 
            ]
 
592
    from bzrlib.tests import test_server
 
593
    return [(LocalTransport, test_server.LocalURLServer),]