~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Jonathan Lange
  • Date: 2009-12-16 06:38:15 UTC
  • mto: This revision was merged to the branch mainline in revision 4907.
  • Revision ID: jml@canonical.com-20091216063815-3p9cc9fzcfj5ten9
RemoveĀ flakes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
42
from bzrlib.transport import Transport, Server
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:
97
105
    def abspath(self, relpath):
98
106
        """Return the full url to the given relative URL."""
99
107
        # TODO: url escape the result. RBC 20060523.
100
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
101
108
        # jam 20060426 Using normpath on the real path, because that ensures
102
109
        #       proper handling of stuff like
103
110
        path = osutils.normpath(osutils.pathjoin(
104
111
                    self._local_base, urlutils.unescape(relpath)))
 
112
        # on windows, our _local_base may or may not have a drive specified
 
113
        # (ie, it may be "/" or "c:/foo").
 
114
        # If 'relpath' is '/' we *always* get back an abspath without
 
115
        # the drive letter - but if our transport already has a drive letter,
 
116
        # we want our abspaths to have a drive letter too - so handle that
 
117
        # here.
 
118
        if (sys.platform == "win32" and self._local_base[1:2] == ":"
 
119
            and path == '/'):
 
120
            path = self._local_base[:3]
 
121
 
105
122
        return urlutils.local_path_to_url(path)
106
123
 
107
124
    def local_abspath(self, relpath):
110
127
        This function only exists for the LocalTransport, since it is
111
128
        the only one that has direct local access.
112
129
        This is mostly for stuff like WorkingTree which needs to know
113
 
        the local working directory.
114
 
        
 
130
        the local working directory.  The returned path will always contain
 
131
        forward slashes as the path separator, regardless of the platform.
 
132
 
115
133
        This function is quite expensive: it calls realpath which resolves
116
134
        symlinks.
117
135
        """
126
144
            abspath = u'.'
127
145
 
128
146
        return urlutils.file_relpath(
129
 
            urlutils.strip_trailing_slash(self.base), 
 
147
            urlutils.strip_trailing_slash(self.base),
130
148
            urlutils.strip_trailing_slash(abspath))
131
149
 
132
150
    def has(self, relpath):
137
155
 
138
156
        :param relpath: The relative path to the file
139
157
        """
 
158
        canonical_url = self.abspath(relpath)
 
159
        if canonical_url in transport._file_streams:
 
160
            transport._file_streams[canonical_url].flush()
140
161
        try:
141
162
            path = self._abspath(relpath)
142
163
            return open(path, 'rb')
143
164
        except (IOError, OSError),e:
 
165
            if e.errno == errno.EISDIR:
 
166
                return LateReadError(relpath)
144
167
            self._translate_error(e, path)
145
168
 
146
169
    def put_file(self, relpath, f, mode=None):
148
171
 
149
172
        :param relpath: Location to put the contents, relative to base.
150
173
        :param f:       File-like object.
151
 
        :param mode: The mode for the newly created file, 
 
174
        :param mode: The mode for the newly created file,
152
175
                     None means just use the default
153
176
        """
154
177
 
160
183
        except (IOError, OSError),e:
161
184
            self._translate_error(e, path)
162
185
        try:
163
 
            self._pump(f, fp)
 
186
            length = self._pump(f, fp)
164
187
            fp.commit()
165
188
        finally:
166
189
            fp.close()
 
190
        return length
167
191
 
168
192
    def put_bytes(self, relpath, bytes, mode=None):
169
193
        """Copy the string into the location.
180
204
        except (IOError, OSError),e:
181
205
            self._translate_error(e, path)
182
206
        try:
183
 
            fp.write(bytes)
 
207
            if bytes:
 
208
                fp.write(bytes)
184
209
            fp.commit()
185
210
        finally:
186
211
            fp.close()
261
286
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
262
287
                             create_parent_dir=False, dir_mode=None):
263
288
        def writer(fd):
264
 
            os.write(fd, bytes)
 
289
            if bytes:
 
290
                os.write(fd, bytes)
265
291
        self._put_non_atomic_helper(relpath, writer, mode=mode,
266
292
                                    create_parent_dir=create_parent_dir,
267
293
                                    dir_mode=dir_mode)
298
324
        """Create a directory at the given path."""
299
325
        self._mkdir(self._abspath(relpath), mode=mode)
300
326
 
 
327
    def open_write_stream(self, relpath, mode=None):
 
328
        """See Transport.open_write_stream."""
 
329
        # initialise the file
 
330
        self.put_bytes_non_atomic(relpath, "", mode=mode)
 
331
        abspath = self._abspath(relpath)
 
332
        handle = open(abspath, 'wb')
 
333
        if mode is not None:
 
334
            self._check_mode_and_size(abspath, handle.fileno(), mode)
 
335
        transport._file_streams[self.abspath(relpath)] = handle
 
336
        return transport.FileFileStream(self, relpath, handle)
 
337
 
301
338
    def _get_append_file(self, relpath, mode=None):
302
339
        """Call os.open() for the given relpath"""
303
340
        file_abspath = self._abspath(relpath)
335
372
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
336
373
        try:
337
374
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
338
 
            os.write(fd, bytes)
 
375
            if bytes:
 
376
                os.write(fd, bytes)
339
377
        finally:
340
378
            os.close(fd)
341
379
        return result
362
400
    def rename(self, rel_from, rel_to):
363
401
        path_from = self._abspath(rel_from)
364
402
        try:
365
 
            # *don't* call bzrlib.osutils.rename, because we want to 
 
403
            # *don't* call bzrlib.osutils.rename, because we want to
366
404
            # detect errors on rename
367
405
            os.rename(path_from, self._abspath(rel_to))
368
406
        except (IOError, OSError),e:
390
428
        except (IOError, OSError),e:
391
429
            self._translate_error(e, path)
392
430
 
 
431
    def external_url(self):
 
432
        """See bzrlib.transport.Transport.external_url."""
 
433
        # File URL's are externally usable.
 
434
        return self.base
 
435
 
393
436
    def copy_to(self, relpaths, other, mode=None, pb=None):
394
437
        """Copy a set of entries from self into another Transport.
395
438
 
490
533
        self._local_base = urlutils._win32_local_path_from_url(base)
491
534
 
492
535
    def abspath(self, relpath):
493
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
494
536
        path = osutils.normpath(osutils.pathjoin(
495
537
                    self._local_base, urlutils.unescape(relpath)))
496
538
        return urlutils._win32_local_path_to_url(path)
497
539
 
498
540
    def clone(self, offset=None):
499
541
        """Return a new LocalTransport with root at self.base + offset
500
 
        Because the local filesystem does not require a connection, 
 
542
        Because the local filesystem does not require a connection,
501
543
        we can just return a new object.
502
544
        """
503
545
        if offset is None:
514
556
 
515
557
class LocalURLServer(Server):
516
558
    """A pretend server for local transports, using file:// urls.
517
 
    
 
559
 
518
560
    Of course no actual server is required to access the local filesystem, so
519
561
    this just exists to tell the test code how to get to it.
520
562
    """
521
563
 
 
564
    def setUp(self):
 
565
        """Setup the server to service requests.
 
566
 
 
567
        :param decorated_transport: ignored by this implementation.
 
568
        """
 
569
 
522
570
    def get_url(self):
523
571
        """See Transport.Server.get_url."""
524
572
        return urlutils.local_path_to_url('')