source: trunk/server/common/oursrc/python-routefs/routefs/__init__.py @ 1464

Last change on this file since 1464 was 838, checked in by andersk, 14 years ago
Package python-routefs.
File size: 5.9 KB
Line 
1"""
2RouteFS is a base class for developing read-only FUSE filesystems that
3lets you focus on the directory tree instead of the system calls.
4
5RouteFS uses the Routes library developed for Pylons. URLs were
6inspired by filesystems, and now you can have filesystems inspired by
7URLs.
8
9When developing a descendent of RouteFS, any methods defined in that
10class are considered "controllers", and receive any other parameters
11specified by the URL as keyword arguments.
12"""
13
14import fuse
15import routes
16import errno
17import stat
18
19fuse.fuse_python_api = (0, 2)
20
21class RouteStat(fuse.Stat):
22    """
23    RouteStat is a descendent of fuse.Stat, defined to make sure that
24    all of the necessary attributes are always defined
25    """
26    def __init__(self):
27        self.st_mode = 0
28        self.st_ino = 0
29        self.st_dev = 0
30        self.st_nlink = 0
31        self.st_uid = 0
32        self.st_gid = 0
33        self.st_size = 0
34        self.st_atime = 0
35        self.st_mtime = 0
36        self.st_ctime = 0
37
38class RouteMeta(type):
39    """
40    Metaclass to calculate controller methods
41   
42    Routes needs to be pre-seeded with a list of "controllers". For
43    all descendents of RouteFS, the list of controllers is defined to
44    be any non-private methods of the class that were not in the
45    RouteFS class.
46    """
47    def __init__(cls, classname, bases, dict_):
48        super(RouteMeta, cls).__init__(classname, bases, dict_)
49        if bases != (fuse.Fuse,):
50            new_funcs = set(dict_.keys()).difference(dir(RouteFS))
51            cls.controllers([func for func in new_funcs \
52                                 if not func.startswith('_')])
53
54class RouteFS(fuse.Fuse):
55    """
56    RouteFS: Web 2.0 for filesystems
57    """
58    __metaclass__ = RouteMeta
59    def __init__(self, *args, **kwargs):
60        super(RouteFS, self).__init__(*args, **kwargs)
61       
62        self.map = self.make_map()
63        self.map.create_regs(self.controller_list)
64       
65    def make_map(self):
66        """
67        This method should be overridden by descendents of RouteFS to
68        define the routing for the filesystem
69        """
70        m = routes.Mapper()
71       
72        m.connect(':controller')
73       
74        return m
75   
76    @classmethod
77    def controllers(cls, lst):
78        cls.controller_list = lst
79   
80    def _get_file(self, path):
81        """
82        Find the filesystem entry object for a given path
83        """
84        match = self.map.match(path)
85        if match is None:
86            return NoEntry()
87        controller = match.pop('controller')
88        result = getattr(self, controller)(**match)
89        if type(result) is str:
90            result = File(result)
91        if type(result) is list:
92            result = Directory(result)
93        return result
94   
95    def readdir(self, path, offset):
96        """
97        If the path referred to is a directory, return the elements of
98        that diectory
99        """
100        return self._get_file(path).readdir(offset)
101   
102    def getattr(self, path):
103        """
104        Return the stat information for a path
105       
106        The stat information for a directory, symlink, or file is
107        predetermined based on which it is.
108        """
109        return self._get_file(path).getattr()
110   
111    def read(self, path, length, offset):
112        """
113        If the path specified is a file, return the requested portion
114        of the file
115        """
116        return self._get_file(path).read(length, offset)
117   
118    def readlink(self, path):
119        """
120        If the path specified is a symlink, return the target
121        """
122        return self._get_file(path).readlink()
123
124class TreeKey(object):
125    def getattr(self):
126        return -errno.EINVAL
127    def readdir(self, offset):
128        return -errno.EINVAL
129    def read(self, length, offset):
130        return -errno.EINVAL
131    def readlink(self):
132        return -errno.EINVAL
133
134class NoEntry(TreeKey):
135    def getattr(self):
136        return -errno.ENOENT
137    def readdir(self, offset):
138        return -errno.ENOENT
139    def read(self, length, offset):
140        return -errno.ENOENT
141    def readlink(self):
142        return -errno.ENOENT
143
144class TreeEntry(TreeKey):
145    default_mode = 0444
146   
147    def __new__(cls, contents, mode=None):
148        return super(TreeEntry, cls).__new__(cls, contents)
149   
150    def __init__(self, contents, mode=None):
151        if mode is None:
152            self.mode = self.default_mode
153        else:
154            self.mode = mode
155       
156        super(TreeEntry, self).__init__(contents)
157
158class Directory(TreeEntry, list):
159    """
160    A dummy class representing a filesystem entry that should be a
161    directory
162    """
163    default_mode = 0555
164
165    def getattr(self):
166        st = RouteStat()
167        st.st_mode = stat.S_IFDIR | self.mode
168        st.st_nlink = 2
169        return st
170
171    def readdir(self, offset):
172        for member in ['.', '..'] + self:
173            yield fuse.Direntry(str(member))
174
175class Symlink(TreeEntry, str):
176    """
177    A dummy class representing something that should be a symlink
178    """
179    default_mode = 0777
180
181    def getattr(self):
182        st = RouteStat()
183        st.st_mode = stat.S_IFLNK | self.mode
184        st.st_nlink = 1
185        st.st_size = len(self)
186        return st
187
188    def readlink(self):
189        return self
190
191class File(TreeEntry, str):
192    """
193    A dummy class representing something that should be a file
194    """
195    default_mode = 0444
196
197    def getattr(self):
198        st = RouteStat()
199        st.st_mode = stat.S_IFREG | self.mode
200        st.st_nlink = 1
201        st.st_size = len(self)
202        return st
203
204    def read(self, length, offset):
205        return self[offset:offset + length]
206
207def main(cls):
208    """
209    A convenience function for initializing a RouteFS filesystem
210    """
211    server = cls(version="%prog " + fuse.__version__,
212                 usage=fuse.Fuse.fusage,
213                 dash_s_do='setsingle')
214    server.parse(values=server, errex=1)
215    server.main()
216
217from dictfs import DictFS
218
219__all__ = ['RouteFS', 'DictFS', 'Symlink', 'Directory', 'File', 'main']
Note: See TracBrowser for help on using the repository browser.