source: trunk/packages/python-routefs/routefs/__init__.py @ 2758

Last change on this file since 2758 was 981, checked in by broder, 16 years ago

Import RouteFS for new versions of ConsoleFS and RemconfFS

File size: 6.0 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 result is None:
90            return NoEntry()
91        if type(result) is str:
92            result = File(result)
93        if type(result) is list:
94            result = Directory(result)
95        return result
96   
97    def readdir(self, path, offset):
98        """
99        If the path referred to is a directory, return the elements of
100        that diectory
101        """
102        return self._get_file(path).readdir(offset)
103   
104    def getattr(self, path):
105        """
106        Return the stat information for a path
107       
108        The stat information for a directory, symlink, or file is
109        predetermined based on which it is.
110        """
111        return self._get_file(path).getattr()
112   
113    def read(self, path, length, offset):
114        """
115        If the path specified is a file, return the requested portion
116        of the file
117        """
118        return self._get_file(path).read(length, offset)
119   
120    def readlink(self, path):
121        """
122        If the path specified is a symlink, return the target
123        """
124        return self._get_file(path).readlink()
125
126class TreeKey(object):
127    def getattr(self):
128        return -errno.EINVAL
129    def readdir(self, offset):
130        return -errno.EINVAL
131    def read(self, length, offset):
132        return -errno.EINVAL
133    def readlink(self):
134        return -errno.EINVAL
135
136class NoEntry(TreeKey):
137    def getattr(self):
138        return -errno.ENOENT
139    def readdir(self, offset):
140        return -errno.ENOENT
141    def read(self, length, offset):
142        return -errno.ENOENT
143    def readlink(self):
144        return -errno.ENOENT
145
146class TreeEntry(TreeKey):
147    default_mode = 0444
148   
149    def __new__(cls, contents, mode=None):
150        return super(TreeEntry, cls).__new__(cls, contents)
151   
152    def __init__(self, contents, mode=None):
153        if mode is None:
154            self.mode = self.default_mode
155        else:
156            self.mode = mode
157       
158        super(TreeEntry, self).__init__(contents)
159
160class Directory(TreeEntry, list):
161    """
162    A dummy class representing a filesystem entry that should be a
163    directory
164    """
165    default_mode = 0555
166
167    def getattr(self):
168        st = RouteStat()
169        st.st_mode = stat.S_IFDIR | self.mode
170        st.st_nlink = 2
171        return st
172
173    def readdir(self, offset):
174        for member in ['.', '..'] + self:
175            yield fuse.Direntry(str(member))
176
177class Symlink(TreeEntry, str):
178    """
179    A dummy class representing something that should be a symlink
180    """
181    default_mode = 0777
182
183    def getattr(self):
184        st = RouteStat()
185        st.st_mode = stat.S_IFLNK | self.mode
186        st.st_nlink = 1
187        st.st_size = len(self)
188        return st
189
190    def readlink(self):
191        return self
192
193class File(TreeEntry, str):
194    """
195    A dummy class representing something that should be a file
196    """
197    default_mode = 0444
198
199    def getattr(self):
200        st = RouteStat()
201        st.st_mode = stat.S_IFREG | self.mode
202        st.st_nlink = 1
203        st.st_size = len(self)
204        return st
205
206    def read(self, length, offset):
207        return self[offset:offset + length]
208
209def main(cls):
210    """
211    A convenience function for initializing a RouteFS filesystem
212    """
213    server = cls(version="%prog " + fuse.__version__,
214                 usage=fuse.Fuse.fusage,
215                 dash_s_do='setsingle')
216    server.parse(values=server, errex=1)
217    server.main()
218
219from dictfs import DictFS
220
221__all__ = ['RouteFS', 'DictFS', 'Symlink', 'Directory', 'File', 'main']
Note: See TracBrowser for help on using the repository browser.