~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

(jelmer) Use the absolute_import feature everywhere in bzrlib,
 and add a source test to make sure it's used everywhere. (Jelmer Vernooij)

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
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):
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
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)
254
257
            if mode is not None and mode != S_IMODE(st.st_mode):
255
258
                # Because of umask, we may still need to chmod the file.
256
259
                # But in the general case, we won't have to
257
 
                os.chmod(abspath, mode)
 
260
                osutils.chmod_if_possible(abspath, mode)
258
261
            writer(fd)
259
262
        finally:
260
263
            os.close(fd)
313
316
            local_mode = mode
314
317
        try:
315
318
            os.mkdir(abspath, local_mode)
316
 
            if mode is not None:
317
 
                # It is probably faster to just do the chmod, rather than
318
 
                # doing a stat, and then trying to compare
319
 
                os.chmod(abspath, mode)
320
319
        except (IOError, OSError),e:
321
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)
322
326
 
323
327
    def mkdir(self, relpath, mode=None):
324
328
        """Create a directory at the given path."""
326
330
 
327
331
    def open_write_stream(self, relpath, mode=None):
328
332
        """See Transport.open_write_stream."""
329
 
        # initialise the file
330
 
        self.put_bytes_non_atomic(relpath, "", mode=mode)
331
333
        abspath = self._abspath(relpath)
332
 
        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()
333
339
        if mode is not None:
334
340
            self._check_mode_and_size(abspath, handle.fileno(), mode)
335
341
        transport._file_streams[self.abspath(relpath)] = handle
354
360
        if mode is not None and mode != S_IMODE(st.st_mode):
355
361
            # Because of umask, we may still need to chmod the file.
356
362
            # But in the general case, we won't have to
357
 
            os.chmod(file_abspath, mode)
 
363
            osutils.chmod_if_possible(file_abspath, mode)
358
364
        return st.st_size
359
365
 
360
366
    def append_file(self, relpath, f, mode=None):
399
405
 
400
406
    def rename(self, rel_from, rel_to):
401
407
        path_from = self._abspath(rel_from)
 
408
        path_to = self._abspath(rel_to)
402
409
        try:
403
410
            # *don't* call bzrlib.osutils.rename, because we want to
404
 
            # detect errors on rename
405
 
            os.rename(path_from, self._abspath(rel_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)
406
414
        except (IOError, OSError),e:
407
415
            # TODO: What about path_to?
408
416
            self._translate_error(e, path_from)
451
459
                    otherpath = other._abspath(path)
452
460
                    shutil.copy(mypath, otherpath)
453
461
                    if mode is not None:
454
 
                        os.chmod(otherpath, mode)
 
462
                        osutils.chmod_if_possible(otherpath, mode)
455
463
                except (IOError, OSError),e:
456
464
                    self._translate_error(e, path)
457
465
                count += 1
481
489
        path = relpath
482
490
        try:
483
491
            path = self._abspath(relpath)
484
 
            return os.stat(path)
 
492
            return os.lstat(path)
485
493
        except (IOError, OSError),e:
486
494
            self._translate_error(e, path)
487
495
 
515
523
        except (IOError, OSError),e:
516
524
            self._translate_error(e, path)
517
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
 
518
551
    def _can_roundtrip_unix_modebits(self):
519
552
        if sys.platform == 'win32':
520
553
            # anyone else?
533
566
        self._local_base = urlutils._win32_local_path_from_url(base)
534
567
 
535
568
    def abspath(self, relpath):
536
 
        path = osutils.normpath(osutils.pathjoin(
 
569
        path = osutils._win32_normpath(osutils.pathjoin(
537
570
                    self._local_base, urlutils.unescape(relpath)))
538
571
        return urlutils._win32_local_path_to_url(path)
539
572
 
554
587
            return EmulatedWin32LocalTransport(abspath)
555
588
 
556
589
 
557
 
class LocalURLServer(Server):
558
 
    """A pretend server for local transports, using file:// urls.
559
 
 
560
 
    Of course no actual server is required to access the local filesystem, so
561
 
    this just exists to tell the test code how to get to it.
562
 
    """
563
 
 
564
 
    def setUp(self):
565
 
        """Setup the server to service requests.
566
 
 
567
 
        :param decorated_transport: ignored by this implementation.
568
 
        """
569
 
 
570
 
    def get_url(self):
571
 
        """See Transport.Server.get_url."""
572
 
        return urlutils.local_path_to_url('')
573
 
 
574
 
 
575
590
def get_test_permutations():
576
591
    """Return the permutations to be used in testing."""
577
 
    return [
578
 
            (LocalTransport, LocalURLServer),
579
 
            ]
 
592
    from bzrlib.tests import test_server
 
593
    return [(LocalTransport, test_server.LocalURLServer),]