~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: John Arbash Meinel
  • Date: 2006-07-30 13:54:37 UTC
  • mto: (1946.2.6 reduce-knit-churn)
  • mto: This revision was merged to the branch mainline in revision 1898.
  • Revision ID: john@arbash-meinel.com-20060730135437-1d722abdb14bff76
(jelmer) Install new intertree tests

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
"""
21
21
 
22
22
import os
23
 
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
 
23
import shutil
24
24
import sys
25
 
 
26
 
from bzrlib.lazy_import import lazy_import
27
 
lazy_import(globals(), """
28
 
import errno
29
 
import shutil
30
 
 
31
 
from bzrlib import (
32
 
    atomicfile,
33
 
    osutils,
34
 
    urlutils,
35
 
    symbol_versioning,
36
 
    )
 
25
from stat import ST_MODE, S_ISDIR, ST_SIZE
 
26
import tempfile
 
27
 
 
28
from bzrlib.osutils import (abspath, realpath, normpath, pathjoin, rename, 
 
29
                            check_legal_path, rmtree)
 
30
from bzrlib.symbol_versioning import warn
37
31
from bzrlib.trace import mutter
38
 
""")
39
 
 
40
32
from bzrlib.transport import Transport, Server
41
 
 
42
 
 
43
 
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
44
 
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
 
33
import bzrlib.urlutils as urlutils
45
34
 
46
35
 
47
36
class LocalTransport(Transport):
50
39
    def __init__(self, base):
51
40
        """Set the base path where files will be stored."""
52
41
        if not base.startswith('file://'):
53
 
            symbol_versioning.warn(
54
 
                "Instantiating LocalTransport with a filesystem path"
 
42
            warn("Instantiating LocalTransport with a filesystem path"
55
43
                " is deprecated as of bzr 0.8."
56
44
                " Please use bzrlib.transport.get_transport()"
57
45
                " or pass in a file:// url.",
63
51
            base = base + '/'
64
52
        super(LocalTransport, self).__init__(base)
65
53
        self._local_base = urlutils.local_path_from_url(base)
 
54
        ## mutter("_local_base: %r => %r", base, self._local_base)
66
55
 
67
56
    def should_cache(self):
68
57
        return False
94
83
        assert isinstance(relpath, basestring), (type(relpath), relpath)
95
84
        # jam 20060426 Using normpath on the real path, because that ensures
96
85
        #       proper handling of stuff like
97
 
        path = osutils.normpath(osutils.pathjoin(
98
 
                    self._local_base, urlutils.unescape(relpath)))
 
86
        path = normpath(pathjoin(self._local_base, urlutils.unescape(relpath)))
99
87
        return urlutils.local_path_to_url(path)
100
88
 
101
89
    def local_abspath(self, relpath):
137
125
        except (IOError, OSError),e:
138
126
            self._translate_error(e, path)
139
127
 
140
 
    def put_file(self, relpath, f, mode=None):
141
 
        """Copy the file-like object into the location.
 
128
    def put(self, relpath, f, mode=None):
 
129
        """Copy the file-like or string object into the location.
142
130
 
143
131
        :param relpath: Location to put the contents, relative to base.
144
 
        :param f:       File-like object.
145
 
        :param mode: The mode for the newly created file, 
146
 
                     None means just use the default
 
132
        :param f:       File-like or string object.
147
133
        """
 
134
        from bzrlib.atomicfile import AtomicFile
148
135
 
149
136
        path = relpath
150
137
        try:
151
138
            path = self._abspath(relpath)
152
 
            osutils.check_legal_path(path)
153
 
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
 
139
            check_legal_path(path)
 
140
            fp = AtomicFile(path, 'wb', new_mode=mode)
154
141
        except (IOError, OSError),e:
155
142
            self._translate_error(e, path)
156
143
        try:
159
146
        finally:
160
147
            fp.close()
161
148
 
162
 
    def put_bytes(self, relpath, bytes, mode=None):
163
 
        """Copy the string into the location.
164
 
 
165
 
        :param relpath: Location to put the contents, relative to base.
166
 
        :param bytes:   String
167
 
        """
168
 
 
169
 
        path = relpath
170
 
        try:
171
 
            path = self._abspath(relpath)
172
 
            osutils.check_legal_path(path)
173
 
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
174
 
        except (IOError, OSError),e:
175
 
            self._translate_error(e, path)
176
 
        try:
177
 
            fp.write(bytes)
178
 
            fp.commit()
179
 
        finally:
180
 
            fp.close()
181
 
 
182
 
    def _put_non_atomic_helper(self, relpath, writer,
183
 
                               mode=None,
184
 
                               create_parent_dir=False,
185
 
                               dir_mode=None):
186
 
        """Common functionality information for the put_*_non_atomic.
187
 
 
188
 
        This tracks all the create_parent_dir stuff.
189
 
 
190
 
        :param relpath: the path we are putting to.
191
 
        :param writer: A function that takes an os level file descriptor
192
 
            and writes whatever data it needs to write there.
193
 
        :param mode: The final file mode.
194
 
        :param create_parent_dir: Should we be creating the parent directory
195
 
            if it doesn't exist?
196
 
        """
197
 
        abspath = self._abspath(relpath)
198
 
        if mode is None:
199
 
            # os.open() will automatically use the umask
200
 
            local_mode = 0666
201
 
        else:
202
 
            local_mode = mode
203
 
        try:
204
 
            fd = os.open(abspath, _put_non_atomic_flags, local_mode)
205
 
        except (IOError, OSError),e:
206
 
            # We couldn't create the file, maybe we need to create
207
 
            # the parent directory, and try again
208
 
            if (not create_parent_dir
209
 
                or e.errno not in (errno.ENOENT,errno.ENOTDIR)):
210
 
                self._translate_error(e, relpath)
211
 
            parent_dir = os.path.dirname(abspath)
212
 
            if not parent_dir:
213
 
                self._translate_error(e, relpath)
214
 
            self._mkdir(parent_dir, mode=dir_mode)
215
 
            # We created the parent directory, lets try to open the
216
 
            # file again
217
 
            try:
218
 
                fd = os.open(abspath, _put_non_atomic_flags, local_mode)
219
 
            except (IOError, OSError), e:
220
 
                self._translate_error(e, relpath)
221
 
        try:
222
 
            st = os.fstat(fd)
223
 
            if mode is not None and mode != S_IMODE(st.st_mode):
224
 
                # Because of umask, we may still need to chmod the file.
225
 
                # But in the general case, we won't have to
226
 
                os.chmod(abspath, mode)
227
 
            writer(fd)
228
 
        finally:
229
 
            os.close(fd)
230
 
 
231
 
    def put_file_non_atomic(self, relpath, f, mode=None,
232
 
                            create_parent_dir=False,
233
 
                            dir_mode=None):
234
 
        """Copy the file-like object into the target location.
235
 
 
236
 
        This function is not strictly safe to use. It is only meant to
237
 
        be used when you already know that the target does not exist.
238
 
        It is not safe, because it will open and truncate the remote
239
 
        file. So there may be a time when the file has invalid contents.
240
 
 
241
 
        :param relpath: The remote location to put the contents.
242
 
        :param f:       File-like object.
243
 
        :param mode:    Possible access permissions for new file.
244
 
                        None means do not set remote permissions.
245
 
        :param create_parent_dir: If we cannot create the target file because
246
 
                        the parent directory does not exist, go ahead and
247
 
                        create it, and then try again.
248
 
        """
249
 
        def writer(fd):
250
 
            self._pump_to_fd(f, fd)
251
 
        self._put_non_atomic_helper(relpath, writer, mode=mode,
252
 
                                    create_parent_dir=create_parent_dir,
253
 
                                    dir_mode=dir_mode)
254
 
 
255
 
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
256
 
                             create_parent_dir=False, dir_mode=None):
257
 
        def writer(fd):
258
 
            os.write(fd, bytes)
259
 
        self._put_non_atomic_helper(relpath, writer, mode=mode,
260
 
                                    create_parent_dir=create_parent_dir,
261
 
                                    dir_mode=dir_mode)
262
 
 
263
149
    def iter_files_recursive(self):
264
150
        """Iter the relative paths of files in the transports sub-tree."""
265
151
        queue = list(self.list_dir(u'.'))
272
158
            else:
273
159
                yield relpath
274
160
 
275
 
    def _mkdir(self, abspath, mode=None):
276
 
        """Create a real directory, filtering through mode"""
277
 
        if mode is None:
278
 
            # os.mkdir() will filter through umask
279
 
            local_mode = 0777
280
 
        else:
281
 
            local_mode = mode
282
 
        try:
283
 
            os.mkdir(abspath, local_mode)
284
 
            if mode is not None:
285
 
                # It is probably faster to just do the chmod, rather than
286
 
                # doing a stat, and then trying to compare
287
 
                os.chmod(abspath, mode)
288
 
        except (IOError, OSError),e:
289
 
            self._translate_error(e, abspath)
290
 
 
291
161
    def mkdir(self, relpath, mode=None):
292
162
        """Create a directory at the given path."""
293
 
        self._mkdir(self._abspath(relpath), mode=mode)
294
 
 
295
 
    def _get_append_file(self, relpath, mode=None):
296
 
        """Call os.open() for the given relpath"""
297
 
        file_abspath = self._abspath(relpath)
298
 
        if mode is None:
299
 
            # os.open() will automatically use the umask
300
 
            local_mode = 0666
301
 
        else:
302
 
            local_mode = mode
 
163
        path = relpath
303
164
        try:
304
 
            return file_abspath, os.open(file_abspath, _append_flags, local_mode)
 
165
            path = self._abspath(relpath)
 
166
            os.mkdir(path)
 
167
            if mode is not None:
 
168
                os.chmod(path, mode)
305
169
        except (IOError, OSError),e:
306
 
            self._translate_error(e, relpath)
307
 
 
308
 
    def _check_mode_and_size(self, file_abspath, fd, mode=None):
309
 
        """Check the mode of the file, and return the current size"""
310
 
        st = os.fstat(fd)
311
 
        if mode is not None and mode != S_IMODE(st.st_mode):
312
 
            # Because of umask, we may still need to chmod the file.
313
 
            # But in the general case, we won't have to
314
 
            os.chmod(file_abspath, mode)
315
 
        return st.st_size
316
 
 
317
 
    def append_file(self, relpath, f, mode=None):
 
170
            self._translate_error(e, path)
 
171
 
 
172
    def append(self, relpath, f, mode=None):
318
173
        """Append the text in the file-like object into the final location."""
319
 
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
320
 
        try:
321
 
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
322
 
            self._pump_to_fd(f, fd)
323
 
        finally:
324
 
            os.close(fd)
325
 
        return result
326
 
 
327
 
    def append_bytes(self, relpath, bytes, mode=None):
328
 
        """Append the text in the string into the final location."""
329
 
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
330
 
        try:
331
 
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
332
 
            os.write(fd, bytes)
333
 
        finally:
334
 
            os.close(fd)
335
 
        return result
336
 
 
337
 
    def _pump_to_fd(self, fromfile, to_fd):
338
 
        """Copy contents of one file to another."""
339
 
        BUFSIZE = 32768
340
 
        while True:
341
 
            b = fromfile.read(BUFSIZE)
342
 
            if not b:
343
 
                break
344
 
            os.write(to_fd, b)
 
174
        abspath = self._abspath(relpath)
 
175
        fp = None
 
176
        try:
 
177
            try:
 
178
                fp = open(abspath, 'ab')
 
179
                # FIXME should we really be chmodding every time ? RBC 20060523
 
180
                if mode is not None:
 
181
                    os.chmod(abspath, mode)
 
182
            except (IOError, OSError),e:
 
183
                self._translate_error(e, relpath)
 
184
            # win32 workaround (tell on an unwritten file returns 0)
 
185
            fp.seek(0, 2)
 
186
            result = fp.tell()
 
187
            self._pump(f, fp)
 
188
        finally:
 
189
            if fp is not None:
 
190
                fp.close()
 
191
        return result
345
192
 
346
193
    def copy(self, rel_from, rel_to):
347
194
        """Copy the item at rel_from to the location at rel_to"""
370
217
 
371
218
        try:
372
219
            # this version will delete the destination if necessary
373
 
            osutils.rename(path_from, path_to)
 
220
            rename(path_from, path_to)
374
221
        except (IOError, OSError),e:
375
222
            # TODO: What about path_to?
376
223
            self._translate_error(e, path_from)
421
268
        """
422
269
        path = self._abspath(relpath)
423
270
        try:
424
 
            entries = os.listdir(path)
 
271
            return [urlutils.escape(entry) for entry in os.listdir(path)]
425
272
        except (IOError, OSError), e:
426
273
            self._translate_error(e, path)
427
 
        return [urlutils.escape(entry) for entry in entries]
428
274
 
429
275
    def stat(self, relpath):
430
276
        """Return the stat information for a file.
474
320
            return True
475
321
 
476
322
 
 
323
class LocalRelpathServer(Server):
 
324
    """A pretend server for local transports, using relpaths."""
 
325
 
 
326
    def get_url(self):
 
327
        """See Transport.Server.get_url."""
 
328
        return "."
 
329
 
 
330
 
 
331
class LocalAbspathServer(Server):
 
332
    """A pretend server for local transports, using absolute paths."""
 
333
 
 
334
    def get_url(self):
 
335
        """See Transport.Server.get_url."""
 
336
        return os.path.abspath("")
 
337
 
 
338
 
477
339
class LocalURLServer(Server):
478
 
    """A pretend server for local transports, using file:// urls.
479
 
    
480
 
    Of course no actual server is required to access the local filesystem, so
481
 
    this just exists to tell the test code how to get to it.
482
 
    """
 
340
    """A pretend server for local transports, using file:// urls."""
483
341
 
484
342
    def get_url(self):
485
343
        """See Transport.Server.get_url."""
488
346
 
489
347
def get_test_permutations():
490
348
    """Return the permutations to be used in testing."""
491
 
    return [
 
349
    return [(LocalTransport, LocalRelpathServer),
 
350
            (LocalTransport, LocalAbspathServer),
492
351
            (LocalTransport, LocalURLServer),
493
352
            ]