~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

  • Committer: Robert Collins
  • Date: 2005-10-03 05:54:35 UTC
  • mto: (1393.1.30)
  • mto: This revision was merged to the branch mainline in revision 1400.
  • Revision ID: robertc@robertcollins.net-20051003055434-c8ebd30d1de10247
move exporting functionality into inventory.py - uncovers bug in symlink support

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
"""Transport for the local filesystem.
18
 
 
19
 
This is a fairly thin wrapper on regular file IO."""
20
 
 
21
 
import os
22
 
import errno
23
 
import shutil
24
 
from stat import ST_MODE, S_ISDIR, ST_SIZE
25
 
import tempfile
26
 
import urllib
27
 
 
28
 
from bzrlib.trace import mutter
 
16
"""Implementation of Transport for the local filesystem.
 
17
"""
 
18
 
29
19
from bzrlib.transport import Transport, register_transport, \
30
20
    TransportError, NoSuchFile, FileExists
31
 
from bzrlib.osutils import abspath
 
21
import os, errno
32
22
 
33
23
class LocalTransportError(TransportError):
34
24
    pass
35
25
 
36
 
 
37
26
class LocalTransport(Transport):
38
27
    """This is the transport agent for local filesystem access."""
39
28
 
44
33
        # realpath is incompatible with symlinks. When we traverse
45
34
        # up we might be able to normpath stuff. RBC 20051003
46
35
        super(LocalTransport, self).__init__(
47
 
            os.path.normpath(abspath(base)))
 
36
            os.path.normpath(os.path.abspath(base)))
48
37
 
49
38
    def should_cache(self):
50
39
        return False
60
49
            return LocalTransport(self.abspath(offset))
61
50
 
62
51
    def abspath(self, relpath):
63
 
        """Return the full url to the given relative URL.
 
52
        """Return the full url to the given relative path.
64
53
        This can be supplied with a string or a list
65
54
        """
66
 
        assert isinstance(relpath, basestring), (type(relpath), relpath)
67
 
        return os.path.join(self.base, urllib.unquote(relpath))
 
55
        if isinstance(relpath, basestring):
 
56
            relpath = [relpath]
 
57
        return os.path.join(self.base, *relpath)
68
58
 
69
59
    def relpath(self, abspath):
70
60
        """Return the local path portion from a given absolute path.
71
61
        """
72
 
        from bzrlib.osutils import relpath
73
 
        if abspath is None:
74
 
            abspath = u'.'
75
 
        return relpath(self.base, abspath)
 
62
        from bzrlib.branch import _relpath
 
63
        return _relpath(self.base, abspath)
76
64
 
77
65
    def has(self, relpath):
78
66
        return os.access(self.abspath(relpath), os.F_OK)
86
74
            path = self.abspath(relpath)
87
75
            return open(path, 'rb')
88
76
        except IOError,e:
89
 
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
90
 
                raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
 
77
            if e.errno == errno.ENOENT:
 
78
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
 
79
            raise LocalTransportError(orig_error=e)
 
80
 
 
81
    def get_partial(self, relpath, start, length=None):
 
82
        """Get just part of a file.
 
83
 
 
84
        :param relpath: Path to the file, relative to base
 
85
        :param start: The starting position to read from
 
86
        :param length: The length to read. A length of None indicates
 
87
                       read to the end of the file.
 
88
        :return: A file-like object containing at least the specified bytes.
 
89
                 Some implementations may return objects which can be read
 
90
                 past this length, but this is not guaranteed.
 
91
        """
 
92
        # LocalTransport.get_partial() doesn't care about the length
 
93
        # argument, because it is using a local file, and thus just
 
94
        # returns the file seek'ed to the appropriate location.
 
95
        try:
 
96
            path = self.abspath(relpath)
 
97
            f = open(path, 'rb')
 
98
            f.seek(start, 0)
 
99
            return f
 
100
        except IOError,e:
 
101
            if e.errno == errno.ENOENT:
 
102
                raise NoSuchFile('File %r does not exist' % path, orig_error=e)
91
103
            raise LocalTransportError(orig_error=e)
92
104
 
93
105
    def put(self, relpath, f):
111
123
        finally:
112
124
            fp.close()
113
125
 
114
 
    def iter_files_recursive(self):
115
 
        """Iter the relative paths of files in the transports sub-tree."""
116
 
        queue = list(self.list_dir(u'.'))
117
 
        while queue:
118
 
            relpath = urllib.quote(queue.pop(0))
119
 
            st = self.stat(relpath)
120
 
            if S_ISDIR(st[ST_MODE]):
121
 
                for i, basename in enumerate(self.list_dir(relpath)):
122
 
                    queue.insert(i, relpath+'/'+basename)
123
 
            else:
124
 
                yield relpath
125
 
 
126
126
    def mkdir(self, relpath):
127
127
        """Create a directory at the given path."""
128
128
        try:
183
183
            count = 0
184
184
            for path in relpaths:
185
185
                self._update_pb(pb, 'copy-to', count, total)
186
 
                try:
187
 
                    shutil.copy(self.abspath(path), other.abspath(path))
188
 
                except IOError, e:
189
 
                    if e.errno in (errno.ENOENT, errno.ENOTDIR):
190
 
                        raise NoSuchFile('File or directory %r does not exist' % path, orig_error=e)
191
 
                    raise LocalTransportError(orig_error=e)
 
186
                shutil.copy(self.abspath(path), other.abspath(path))
192
187
                count += 1
193
188
            return count
194
189
        else:
195
190
            return super(LocalTransport, self).copy_to(relpaths, other, pb=pb)
196
191
 
197
 
    def listable(self):
198
 
        """See Transport.listable."""
199
 
        return True
200
192
 
201
193
    def list_dir(self, relpath):
202
194
        """Return a list of all files at the given location.
232
224
        from bzrlib.lock import WriteLock
233
225
        return WriteLock(self.abspath(relpath))
234
226
 
235
 
 
236
 
class ScratchTransport(LocalTransport):
237
 
    """A transport that works in a temporary dir and cleans up after itself.
238
 
    
239
 
    The dir only exists for the lifetime of the Python object.
240
 
    Obviously you should not put anything precious in it.
241
 
    """
242
 
 
243
 
    def __init__(self, base=None):
244
 
        if base is None:
245
 
            base = tempfile.mkdtemp()
246
 
        super(ScratchTransport, self).__init__(base)
247
 
 
248
 
    def __del__(self):
249
 
        shutil.rmtree(self.base, ignore_errors=True)
250
 
        mutter("%r destroyed" % self)
 
227
# If nothing else matches, try the LocalTransport
 
228
register_transport(None, LocalTransport)
 
229
register_transport('file://', LocalTransport)