~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Mark Hammond
  • Date: 2008-09-04 12:03:01 UTC
  • mto: This revision was merged to the branch mainline in revision 3697.
  • Revision ID: mhammond@skippinet.com.au-20080904120301-x3tujl7hloy0o75j
build the list of icons without changing directory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012, 2016 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
 
19
19
This is a fairly thin wrapper on regular file IO.
20
20
"""
21
21
 
22
 
from __future__ import absolute_import
23
 
 
24
22
import os
25
 
from stat import ST_MODE, S_ISDIR, S_IMODE
 
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
26
24
import sys
27
25
 
28
26
from bzrlib.lazy_import import lazy_import
35
33
    osutils,
36
34
    urlutils,
37
35
    symbol_versioning,
 
36
    transport,
38
37
    )
 
38
from bzrlib.trace import mutter
39
39
from bzrlib.transport import LateReadError
40
40
""")
41
41
 
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):
 
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):
50
50
    """This is the transport agent for local filesystem access."""
51
51
 
52
52
    def __init__(self, base):
53
53
        """Set the base path where files will be stored."""
54
54
        if not base.startswith('file://'):
55
 
            raise AssertionError("not a file:// url: %r" % base)
 
55
            symbol_versioning.warn(
 
56
                "Instantiating LocalTransport with a filesystem path"
 
57
                " is deprecated as of bzr 0.8."
 
58
                " Please use bzrlib.transport.get_transport()"
 
59
                " or pass in a file:// url.",
 
60
                 DeprecationWarning,
 
61
                 stacklevel=2
 
62
                 )
 
63
            base = urlutils.local_path_to_url(base)
56
64
        if base[-1] != '/':
57
65
            base = base + '/'
58
66
 
63
71
            self._local_base = ''
64
72
            super(LocalTransport, self).__init__(base)
65
73
            return
66
 
 
 
74
            
67
75
        super(LocalTransport, self).__init__(base)
68
76
        self._local_base = urlutils.local_path_from_url(base)
69
 
        if self._local_base[-1] != '/':
70
 
            self._local_base = self._local_base + '/'
71
77
 
72
78
    def clone(self, offset=None):
73
79
        """Return a new LocalTransport with root at self.base + offset
74
 
        Because the local filesystem does not require a connection,
 
80
        Because the local filesystem does not require a connection, 
75
81
        we can just return a new object.
76
82
        """
77
83
        if offset is None:
93
99
         - relative_reference is url escaped.
94
100
        """
95
101
        if relative_reference in ('.', ''):
96
 
            # _local_base normally has a trailing slash; strip it so that stat
97
 
            # on a transport pointing to a symlink reads the link not the
98
 
            # referent but be careful of / and c:\
99
 
            return osutils.split(self._local_base)[0]
 
102
            return self._local_base
100
103
        return self._local_base + urlutils.unescape(relative_reference)
101
104
 
102
105
    def abspath(self, relpath):
106
109
        #       proper handling of stuff like
107
110
        path = osutils.normpath(osutils.pathjoin(
108
111
                    self._local_base, urlutils.unescape(relpath)))
109
 
        # on windows, our _local_base may or may not have a drive specified
110
 
        # (ie, it may be "/" or "c:/foo").
111
 
        # If 'relpath' is '/' we *always* get back an abspath without
112
 
        # the drive letter - but if our transport already has a drive letter,
113
 
        # we want our abspaths to have a drive letter too - so handle that
114
 
        # here.
115
 
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
116
 
            and path == '/'):
117
 
            path = self._local_base[:3]
118
 
 
119
112
        return urlutils.local_path_to_url(path)
120
113
 
121
114
    def local_abspath(self, relpath):
140
133
        if abspath is None:
141
134
            abspath = u'.'
142
135
 
143
 
        return urlutils.file_relpath(self.base, abspath)
 
136
        return urlutils.file_relpath(
 
137
            urlutils.strip_trailing_slash(self.base),
 
138
            urlutils.strip_trailing_slash(abspath))
144
139
 
145
140
    def has(self, relpath):
146
141
        return os.access(self._abspath(relpath), os.F_OK)
155
150
            transport._file_streams[canonical_url].flush()
156
151
        try:
157
152
            path = self._abspath(relpath)
158
 
            return osutils.open_file(path, 'rb')
 
153
            return open(path, 'rb')
159
154
        except (IOError, OSError),e:
160
155
            if e.errno == errno.EISDIR:
161
156
                return LateReadError(relpath)
166
161
 
167
162
        :param relpath: Location to put the contents, relative to base.
168
163
        :param f:       File-like object.
169
 
        :param mode: The mode for the newly created file,
 
164
        :param mode: The mode for the newly created file, 
170
165
                     None means just use the default
171
166
        """
172
167
 
184
179
            fp.close()
185
180
        return length
186
181
 
187
 
    def put_bytes(self, relpath, raw_bytes, mode=None):
 
182
    def put_bytes(self, relpath, bytes, mode=None):
188
183
        """Copy the string into the location.
189
184
 
190
185
        :param relpath: Location to put the contents, relative to base.
191
 
        :param raw_bytes:   String
 
186
        :param bytes:   String
192
187
        """
193
 
        if not isinstance(raw_bytes, str):
194
 
            raise TypeError(
195
 
                'raw_bytes must be a plain string, not %s' % type(raw_bytes))
 
188
 
196
189
        path = relpath
197
190
        try:
198
191
            path = self._abspath(relpath)
201
194
        except (IOError, OSError),e:
202
195
            self._translate_error(e, path)
203
196
        try:
204
 
            if bytes:
205
 
                fp.write(raw_bytes)
 
197
            fp.write(bytes)
206
198
            fp.commit()
207
199
        finally:
208
200
            fp.close()
251
243
            if mode is not None and mode != S_IMODE(st.st_mode):
252
244
                # Because of umask, we may still need to chmod the file.
253
245
                # But in the general case, we won't have to
254
 
                osutils.chmod_if_possible(abspath, mode)
 
246
                os.chmod(abspath, mode)
255
247
            writer(fd)
256
248
        finally:
257
249
            os.close(fd)
283
275
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
284
276
                             create_parent_dir=False, dir_mode=None):
285
277
        def writer(fd):
286
 
            if bytes:
287
 
                os.write(fd, bytes)
 
278
            os.write(fd, bytes)
288
279
        self._put_non_atomic_helper(relpath, writer, mode=mode,
289
280
                                    create_parent_dir=create_parent_dir,
290
281
                                    dir_mode=dir_mode)
310
301
            local_mode = mode
311
302
        try:
312
303
            os.mkdir(abspath, local_mode)
 
304
            if mode is not None:
 
305
                # It is probably faster to just do the chmod, rather than
 
306
                # doing a stat, and then trying to compare
 
307
                os.chmod(abspath, mode)
313
308
        except (IOError, OSError),e:
314
309
            self._translate_error(e, abspath)
315
 
        if mode is not None:
316
 
            try:
317
 
                osutils.chmod_if_possible(abspath, mode)
318
 
            except (IOError, OSError), e:
319
 
                self._translate_error(e, abspath)
320
310
 
321
311
    def mkdir(self, relpath, mode=None):
322
312
        """Create a directory at the given path."""
324
314
 
325
315
    def open_write_stream(self, relpath, mode=None):
326
316
        """See Transport.open_write_stream."""
 
317
        # initialise the file
 
318
        self.put_bytes_non_atomic(relpath, "", mode=mode)
327
319
        abspath = self._abspath(relpath)
328
 
        try:
329
 
            handle = osutils.open_file(abspath, 'wb')
330
 
        except (IOError, OSError),e:
331
 
            self._translate_error(e, abspath)
332
 
        handle.truncate()
 
320
        handle = open(abspath, 'wb')
333
321
        if mode is not None:
334
322
            self._check_mode_and_size(abspath, handle.fileno(), mode)
335
323
        transport._file_streams[self.abspath(relpath)] = handle
354
342
        if mode is not None and mode != S_IMODE(st.st_mode):
355
343
            # Because of umask, we may still need to chmod the file.
356
344
            # But in the general case, we won't have to
357
 
            osutils.chmod_if_possible(file_abspath, mode)
 
345
            os.chmod(file_abspath, mode)
358
346
        return st.st_size
359
347
 
360
348
    def append_file(self, relpath, f, mode=None):
372
360
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
373
361
        try:
374
362
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
375
 
            if bytes:
376
 
                os.write(fd, bytes)
 
363
            os.write(fd, bytes)
377
364
        finally:
378
365
            os.close(fd)
379
366
        return result
399
386
 
400
387
    def rename(self, rel_from, rel_to):
401
388
        path_from = self._abspath(rel_from)
402
 
        path_to = self._abspath(rel_to)
403
389
        try:
404
 
            # *don't* call bzrlib.osutils.rename, because we want to
405
 
            # detect conflicting names on rename, and osutils.rename tries to
406
 
            # mask cross-platform differences there
407
 
            os.rename(path_from, path_to)
 
390
            # *don't* call bzrlib.osutils.rename, because we want to 
 
391
            # detect errors on rename
 
392
            os.rename(path_from, self._abspath(rel_to))
408
393
        except (IOError, OSError),e:
409
394
            # TODO: What about path_to?
410
395
            self._translate_error(e, path_from)
453
438
                    otherpath = other._abspath(path)
454
439
                    shutil.copy(mypath, otherpath)
455
440
                    if mode is not None:
456
 
                        osutils.chmod_if_possible(otherpath, mode)
 
441
                        os.chmod(otherpath, mode)
457
442
                except (IOError, OSError),e:
458
443
                    self._translate_error(e, path)
459
444
                count += 1
483
468
        path = relpath
484
469
        try:
485
470
            path = self._abspath(relpath)
486
 
            return os.lstat(path)
 
471
            return os.stat(path)
487
472
        except (IOError, OSError),e:
488
473
            self._translate_error(e, path)
489
474
 
517
502
        except (IOError, OSError),e:
518
503
            self._translate_error(e, path)
519
504
 
520
 
    if osutils.host_os_dereferences_symlinks():
521
 
        def readlink(self, relpath):
522
 
            """See Transport.readlink."""
523
 
            return osutils.readlink(self._abspath(relpath))
524
 
 
525
 
    if osutils.hardlinks_good():
526
 
        def hardlink(self, source, link_name):
527
 
            """See Transport.link."""
528
 
            try:
529
 
                os.link(self._abspath(source), self._abspath(link_name))
530
 
            except (IOError, OSError), e:
531
 
                self._translate_error(e, source)
532
 
 
533
 
    if osutils.has_symlinks():
534
 
        def symlink(self, source, link_name):
535
 
            """See Transport.symlink."""
536
 
            abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
537
 
            source_rel = urlutils.file_relpath(
538
 
                abs_link_dirpath, self.abspath(source))
539
 
 
540
 
            try:
541
 
                os.symlink(source_rel, self._abspath(link_name))
542
 
            except (IOError, OSError), e:
543
 
                self._translate_error(e, source_rel)
544
 
 
545
505
    def _can_roundtrip_unix_modebits(self):
546
506
        if sys.platform == 'win32':
547
507
            # anyone else?
560
520
        self._local_base = urlutils._win32_local_path_from_url(base)
561
521
 
562
522
    def abspath(self, relpath):
563
 
        path = osutils._win32_normpath(osutils.pathjoin(
 
523
        path = osutils.normpath(osutils.pathjoin(
564
524
                    self._local_base, urlutils.unescape(relpath)))
565
525
        return urlutils._win32_local_path_to_url(path)
566
526
 
567
527
    def clone(self, offset=None):
568
528
        """Return a new LocalTransport with root at self.base + offset
569
 
        Because the local filesystem does not require a connection,
 
529
        Because the local filesystem does not require a connection, 
570
530
        we can just return a new object.
571
531
        """
572
532
        if offset is None:
581
541
            return EmulatedWin32LocalTransport(abspath)
582
542
 
583
543
 
 
544
class LocalURLServer(Server):
 
545
    """A pretend server for local transports, using file:// urls.
 
546
    
 
547
    Of course no actual server is required to access the local filesystem, so
 
548
    this just exists to tell the test code how to get to it.
 
549
    """
 
550
 
 
551
    def setUp(self):
 
552
        """Setup the server to service requests.
 
553
        
 
554
        :param decorated_transport: ignored by this implementation.
 
555
        """
 
556
 
 
557
    def get_url(self):
 
558
        """See Transport.Server.get_url."""
 
559
        return urlutils.local_path_to_url('')
 
560
 
 
561
 
584
562
def get_test_permutations():
585
563
    """Return the permutations to be used in testing."""
586
 
    from bzrlib.tests import test_server
587
 
    return [(LocalTransport, test_server.LocalURLServer),]
 
564
    return [
 
565
            (LocalTransport, LocalURLServer),
 
566
            ]