~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Robert Collins
  • Date: 2009-07-07 04:32:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4524.
  • Revision ID: robertc@robertcollins.net-20090707043213-4hjjhgr40iq7gk2d
More informative assertions in xml serialisation.

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
38
39
from bzrlib.transport import LateReadError
62
63
            base = urlutils.local_path_to_url(base)
63
64
        if base[-1] != '/':
64
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
 
65
75
        super(LocalTransport, self).__init__(base)
66
76
        self._local_base = urlutils.local_path_from_url(base)
67
77
 
68
 
    def should_cache(self):
69
 
        return False
70
 
 
71
78
    def clone(self, offset=None):
72
79
        """Return a new LocalTransport with root at self.base + offset
73
 
        Because the local filesystem does not require a connection, 
 
80
        Because the local filesystem does not require a connection,
74
81
        we can just return a new object.
75
82
        """
76
83
        if offset is None:
98
105
    def abspath(self, relpath):
99
106
        """Return the full url to the given relative URL."""
100
107
        # TODO: url escape the result. RBC 20060523.
101
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
102
108
        # jam 20060426 Using normpath on the real path, because that ensures
103
109
        #       proper handling of stuff like
104
110
        path = osutils.normpath(osutils.pathjoin(
105
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
 
106
122
        return urlutils.local_path_to_url(path)
107
123
 
108
124
    def local_abspath(self, relpath):
111
127
        This function only exists for the LocalTransport, since it is
112
128
        the only one that has direct local access.
113
129
        This is mostly for stuff like WorkingTree which needs to know
114
 
        the local working directory.
115
 
        
 
130
        the local working directory.  The returned path will always contain
 
131
        forward slashes as the path separator, regardless of the platform.
 
132
 
116
133
        This function is quite expensive: it calls realpath which resolves
117
134
        symlinks.
118
135
        """
127
144
            abspath = u'.'
128
145
 
129
146
        return urlutils.file_relpath(
130
 
            urlutils.strip_trailing_slash(self.base), 
 
147
            urlutils.strip_trailing_slash(self.base),
131
148
            urlutils.strip_trailing_slash(abspath))
132
149
 
133
150
    def has(self, relpath):
138
155
 
139
156
        :param relpath: The relative path to the file
140
157
        """
 
158
        canonical_url = self.abspath(relpath)
 
159
        if canonical_url in transport._file_streams:
 
160
            transport._file_streams[canonical_url].flush()
141
161
        try:
142
162
            path = self._abspath(relpath)
143
163
            return open(path, 'rb')
151
171
 
152
172
        :param relpath: Location to put the contents, relative to base.
153
173
        :param f:       File-like object.
154
 
        :param mode: The mode for the newly created file, 
 
174
        :param mode: The mode for the newly created file,
155
175
                     None means just use the default
156
176
        """
157
177
 
163
183
        except (IOError, OSError),e:
164
184
            self._translate_error(e, path)
165
185
        try:
166
 
            self._pump(f, fp)
 
186
            length = self._pump(f, fp)
167
187
            fp.commit()
168
188
        finally:
169
189
            fp.close()
 
190
        return length
170
191
 
171
192
    def put_bytes(self, relpath, bytes, mode=None):
172
193
        """Copy the string into the location.
183
204
        except (IOError, OSError),e:
184
205
            self._translate_error(e, path)
185
206
        try:
186
 
            fp.write(bytes)
 
207
            if bytes:
 
208
                fp.write(bytes)
187
209
            fp.commit()
188
210
        finally:
189
211
            fp.close()
264
286
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
265
287
                             create_parent_dir=False, dir_mode=None):
266
288
        def writer(fd):
267
 
            os.write(fd, bytes)
 
289
            if bytes:
 
290
                os.write(fd, bytes)
268
291
        self._put_non_atomic_helper(relpath, writer, mode=mode,
269
292
                                    create_parent_dir=create_parent_dir,
270
293
                                    dir_mode=dir_mode)
301
324
        """Create a directory at the given path."""
302
325
        self._mkdir(self._abspath(relpath), mode=mode)
303
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
 
304
338
    def _get_append_file(self, relpath, mode=None):
305
339
        """Call os.open() for the given relpath"""
306
340
        file_abspath = self._abspath(relpath)
338
372
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
339
373
        try:
340
374
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
341
 
            os.write(fd, bytes)
 
375
            if bytes:
 
376
                os.write(fd, bytes)
342
377
        finally:
343
378
            os.close(fd)
344
379
        return result
365
400
    def rename(self, rel_from, rel_to):
366
401
        path_from = self._abspath(rel_from)
367
402
        try:
368
 
            # *don't* call bzrlib.osutils.rename, because we want to 
 
403
            # *don't* call bzrlib.osutils.rename, because we want to
369
404
            # detect errors on rename
370
405
            os.rename(path_from, self._abspath(rel_to))
371
406
        except (IOError, OSError),e:
393
428
        except (IOError, OSError),e:
394
429
            self._translate_error(e, path)
395
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
 
396
436
    def copy_to(self, relpaths, other, mode=None, pb=None):
397
437
        """Copy a set of entries from self into another Transport.
398
438
 
493
533
        self._local_base = urlutils._win32_local_path_from_url(base)
494
534
 
495
535
    def abspath(self, relpath):
496
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
497
536
        path = osutils.normpath(osutils.pathjoin(
498
537
                    self._local_base, urlutils.unescape(relpath)))
499
538
        return urlutils._win32_local_path_to_url(path)
500
539
 
501
540
    def clone(self, offset=None):
502
541
        """Return a new LocalTransport with root at self.base + offset
503
 
        Because the local filesystem does not require a connection, 
 
542
        Because the local filesystem does not require a connection,
504
543
        we can just return a new object.
505
544
        """
506
545
        if offset is None:
517
556
 
518
557
class LocalURLServer(Server):
519
558
    """A pretend server for local transports, using file:// urls.
520
 
    
 
559
 
521
560
    Of course no actual server is required to access the local filesystem, so
522
561
    this just exists to tell the test code how to get to it.
523
562
    """
524
563
 
525
564
    def setUp(self):
526
565
        """Setup the server to service requests.
527
 
        
 
566
 
528
567
        :param decorated_transport: ignored by this implementation.
529
568
        """
530
569