summaryrefslogtreecommitdiff
path: root/fs/multifs.py
blob: 3454e648e044d3747ccedf033cb5508f64e8c562 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/usr/in/env python

from fs.base import FS, FSError
from fs.path import *


class MultiFS(FS):

    """A MultiFS is a filesystem that delegates to a sequence of other filesystems.
    Operations on the MultiFS will try each 'child' filesystem in order, until it
    succeeds. In effect, creating a filesystem that combines the files and dirs of
    its children.

    """

    def __init__(self):
        FS.__init__(self, thread_synchronize=True)

        self.fs_sequence = []
        self.fs_lookup =  {}

    def __str__(self):
        self._lock.acquire()
        try:
            return "<MultiFS: %s>" % ", ".join(str(fs) for fs in self.fs_sequence)
        finally:
            self._lock.release()

    __repr__ = __str__

    def __unicode__(self):
        return unicode(self.__str__())


    def addfs(self, name, fs):
        """Adds a filesystem to the MultiFS.

        name -- A unique name to refer to the filesystem being added
        fs -- The filesystem to add

        """
        self._lock.acquire()
        try:
            if name in self.fs_lookup:
                raise ValueError("Name already exists.")

            self.fs_sequence.append(fs)
            self.fs_lookup[name] = fs
        finally:
            self._lock.release()

    def removefs(self, name):
        """Removes a filesystem from the sequence.

        name -- The name of the filesystem, as used in addfs

        """
        self._lock.acquire()
        try:
            if name not in self.fs_lookup:
                raise ValueError("No filesystem called '%s'"%name)
            fs = self.fs_lookup[name]
            self.fs_sequence.remove(fs)
            del self.fs_lookup[name]
        finally:
            self._lock.release()

    def __getitem__(self, name):
        self._lock.acquire()
        try:
            return self.fs_lookup[name]
        finally:
            self._lock.release()

    def __iter__(self):
        self._lock.acquire()
        try:
            return iter(self.fs_sequence[:])
        finally:
            self._lock.release()


    def _delegate_search(self, path):
        for fs in self:
            if fs.exists(path):
                return fs
        return None

    def which(self, path):
        """Retrieves the filesystem that a given path would delegate to.
        Returns a tuple of the filesystem's name and the filesystem object itself.

        path -- A path in MultiFS

        """
        self._lock.acquire()
        try:
            for fs in self:
                if fs.exists(path):
                    for fs_name, fs_object in self.fs_lookup.iteritems():
                        if fs is fs_object:
                            return fs_name, fs
            raise ResourceNotFoundError(path, msg="Path does not map to any filesystem: %(path)s")
        finally:
            self._lock.release()

    def getsyspath(self, path, allow_none=False):
        self._lock.acquire()
        try:
            fs = self._delegate_search(path)
            if fs is not None:
                return fs.getsyspath(path, allow_none=allow_none)
            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()

    def desc(self, path):
        self._lock.acquire()
        try:
            if not self.exists(path):
                raise ResourceNotFoundError(path)

            name, fs = self.which(path)
            if name is None:
                return ""
            return "%s, on %s (%s)" % (fs.desc(path), name, fs)
        finally:
            self._lock.release()


    def open(self, path, mode="r",**kwargs):
        self._lock.acquire()
        try:
            for fs in self:
                if fs.exists(path):
                    fs_file = fs.open(path, mode, **kwargs)
                    return fs_file

            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()

    def exists(self, path):
        self._lock.acquire()
        try:
            return self._delegate_search(path) is not None
        finally:
            self._lock.release()

    def isdir(self, path):
        self._lock.acquire()
        try:
            fs = self._delegate_search(path)
            if fs is not None:
                return fs.isdir(path)
            return False
        finally:
            self._lock.release()

    def isfile(self, path):
        self._lock.acquire()
        try:
            fs = self._delegate_search(path)
            if fs is not None:
                return fs.isfile(path)
            return False
        finally:
            self._lock.release()

    def listdir(self, path="./", *args, **kwargs):
        self._lock.acquire()
        try:
            paths = []
            for fs in self:
                try:
                    paths += fs.listdir(path, *args, **kwargs)
                except FSError, e:
                    pass

            return list(set(paths))
        finally:
            self._lock.release()

    def remove(self, path):
        self._lock.acquire()
        try:
            for fs in self:
                if fs.exists(path):
                    fs.remove(path)
                    return
            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()

    def removedir(self, path, recursive=False):
        self._lock.acquire()
        try:
            for fs in self:
                if fs.isdir(path):
                    fs.removedir(path, recursive)
                    return
            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()

    def rename(self, src, dst):
        if not issamedir(src, dst):
            raise ValueError("Destination path must the same directory (use the move method for moving to a different directory)")
        self._lock.acquire()
        try:
            for fs in self:
                if fs.exists(src):
                    fs.rename(src, dst)
                    return
            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()

    def getinfo(self, path):
        self._lock.acquire()
        try:
            for fs in self:
                if fs.exists(path):
                    return fs.getinfo(path)

            raise ResourceNotFoundError(path)
        finally:
            self._lock.release()