Merge branch 'master' into debian
authorEvan Broder <broder@mit.edu>
Sun, 21 Feb 2010 16:13:29 +0000 (08:13 -0800)
committerEvan Broder <broder@mit.edu>
Sun, 21 Feb 2010 16:13:29 +0000 (08:13 -0800)
.gitignore
routefs/__init__.py
routefs/dictfs.py
routefs/examples/dictexfs.py
routefs/examples/homefs.py [new file with mode: 0755]
routefs/examples/pyhesiodfs.py
setup.py

index 70ba289..60000fa 100644 (file)
@@ -2,3 +2,4 @@ RouteFS.egg-info
 build
 dist
 *.pyc
+.DS_Store
\ No newline at end of file
index c3b0357..294ec93 100644 (file)
@@ -5,19 +5,19 @@ lets you focus on the directory tree instead of the system calls.
 RouteFS uses the Routes library developed for Pylons. URLs were
 inspired by filesystems, and now you can have filesystems inspired by
 URLs.
-
-When developing a descendent of RouteFS, any methods defined in that
-class are considered "controllers", and receive any other parameters
-specified by the URL as keyword arguments.
 """
 
-import fuse
-import routes
+
 import errno
 import stat
 
+import fuse
+import routes
+
+
 fuse.fuse_python_api = (0, 2)
 
+
 class RouteStat(fuse.Stat):
     """
     RouteStat is a descendent of fuse.Stat, defined to make sure that
@@ -35,48 +35,33 @@ class RouteStat(fuse.Stat):
         self.st_mtime = 0
         self.st_ctime = 0
 
-class RouteMeta(type):
-    """
-    Metaclass to calculate controller methods
-    
-    Routes needs to be pre-seeded with a list of "controllers". For
-    all descendents of RouteFS, the list of controllers is defined to
-    be any non-private methods of the class that were not in the
-    RouteFS class.
-    """
-    def __init__(cls, classname, bases, dict_):
-        super(RouteMeta, cls).__init__(classname, bases, dict_)
-        if bases != (fuse.Fuse,):
-            new_funcs = set(dict_.keys()).difference(dir(RouteFS))
-            cls.controllers([func for func in new_funcs \
-                                 if not func.startswith('_')])
 
 class RouteFS(fuse.Fuse):
     """
     RouteFS: Web 2.0 for filesystems
+
+    Any method that will be used as the controller in a Routes mapping
+    (either by explicitly specifying the controller or by using the
+    ':controller' variable) must be added to RouteFS.controllers
     """
-    __metaclass__ = RouteMeta
+    controllers = []
     def __init__(self, *args, **kwargs):
         super(RouteFS, self).__init__(*args, **kwargs)
-        
+
         self.map = self.make_map()
-        self.map.create_regs(self.controller_list)
-        
+        self.map.create_regs(self.controllers)
+
     def make_map(self):
         """
         This method should be overridden by descendents of RouteFS to
         define the routing for the filesystem
         """
         m = routes.Mapper()
-        
+
         m.connect(':controller')
-        
+
         return m
-    
-    @classmethod
-    def controllers(cls, lst):
-        cls.controller_list = lst
-    
+
     def _get_file(self, path):
         """
         Find the filesystem entry object for a given path
@@ -93,36 +78,44 @@ class RouteFS(fuse.Fuse):
         if type(result) is list:
             result = Directory(result)
         return result
-    
+
     def readdir(self, path, offset):
         """
         If the path referred to is a directory, return the elements of
         that diectory
         """
         return self._get_file(path).readdir(offset)
-    
+
     def getattr(self, path):
         """
         Return the stat information for a path
-        
+
         The stat information for a directory, symlink, or file is
         predetermined based on which it is.
         """
         return self._get_file(path).getattr()
-    
+
     def read(self, path, length, offset):
         """
         If the path specified is a file, return the requested portion
         of the file
         """
         return self._get_file(path).read(length, offset)
-    
+
     def readlink(self, path):
         """
         If the path specified is a symlink, return the target
         """
         return self._get_file(path).readlink()
 
+    def write(self, path, buf, offset):
+        """
+        If the path specified is a file, call the appropriate member 
+        on the file
+        """
+        return self._get_file(path).write(buf, offset)
+
+
 class TreeKey(object):
     def getattr(self):
         return -errno.EINVAL
@@ -132,6 +125,9 @@ class TreeKey(object):
         return -errno.EINVAL
     def readlink(self):
         return -errno.EINVAL
+    def write(self, length, offset):
+        return -errno.EINVAL
+
 
 class NoEntry(TreeKey):
     def getattr(self):
@@ -142,21 +138,25 @@ class NoEntry(TreeKey):
         return -errno.ENOENT
     def readlink(self):
         return -errno.ENOENT
+    def write(self, length, offset):
+        return -errno.ENOENT
+
 
 class TreeEntry(TreeKey):
     default_mode = 0444
-    
+
     def __new__(cls, contents, mode=None):
         return super(TreeEntry, cls).__new__(cls, contents)
-    
+
     def __init__(self, contents, mode=None):
         if mode is None:
             self.mode = self.default_mode
         else:
             self.mode = mode
-        
+
         super(TreeEntry, self).__init__(contents)
 
+
 class Directory(TreeEntry, list):
     """
     A dummy class representing a filesystem entry that should be a
@@ -174,6 +174,7 @@ class Directory(TreeEntry, list):
         for member in ['.', '..'] + self:
             yield fuse.Direntry(str(member))
 
+
 class Symlink(TreeEntry, str):
     """
     A dummy class representing something that should be a symlink
@@ -190,6 +191,7 @@ class Symlink(TreeEntry, str):
     def readlink(self):
         return self
 
+
 class File(TreeEntry, str):
     """
     A dummy class representing something that should be a file
@@ -206,6 +208,7 @@ class File(TreeEntry, str):
     def read(self, length, offset):
         return self[offset:offset + length]
 
+
 def main(cls):
     """
     A convenience function for initializing a RouteFS filesystem
@@ -216,6 +219,7 @@ def main(cls):
     server.parse(values=server, errex=1)
     server.main()
 
+
 from dictfs import DictFS
 
 __all__ = ['RouteFS', 'DictFS', 'Symlink', 'Directory', 'File', 'main']
index b36c589..25904b1 100644 (file)
@@ -10,38 +10,44 @@ A dictionary represents a directory, with keys corresponding to
 file names and the values corresponding to the file contents.
 """
 
-import routefs
-from routes import Mapper
+
 import os
 
+from routes import Mapper
+
+import routefs
+
+
 class DictFS(routefs.RouteFS):
+    controllers = ['handler']
+
     @property
     def files(self):
         """
         This property should be overridden in your DictFS descendant
         """
         return dict()
-    
+
     def make_map(self):
         m = Mapper()
-        
+
         m.connect('*path', controller='handler')
-        
+
         return m
-    
+
     def handler(self, path, **kwargs):
         if path != '':
             elements = path.split(os.path.sep)
         else:
             elements = []
-        
+
         try:
             tree = self.files
             for elt in elements:
                 tree = tree[elt]
         except KeyError:
             return
-        
+
         if type(tree) is dict:
             return tree.keys()
         else:
index 5533f04..5402e7d 100755 (executable)
@@ -1,10 +1,13 @@
 #!/usr/bin/python
 
+
 import routefs
 
+
 class DictExFS(routefs.DictFS):
     files = dict(Hello='World',
                  Directory=dict(a='a', b='b', c=routefs.Symlink('a')))
 
+
 if __name__ == '__main__':
     routefs.main(DictExFS)
diff --git a/routefs/examples/homefs.py b/routefs/examples/homefs.py
new file mode 100755 (executable)
index 0000000..a832c39
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+"""
+RouteFS Example: HomeFS
+
+If you work on a system where home directories are on network storage
+(i.e. not in /home), mount HomeFS on /home. It's an automounter that
+will automatically create symlinks from user -> their homedir whenever
+/home/user is accessed in any way.
+"""
+
+
+import pwd
+
+from routes import Mapper
+
+import routefs
+
+
+class HomeFS(routefs.RouteFS):
+    controllers = ['getList', 'getUser']
+    def __init__(self, *args, **kwargs):
+        super(HomeFS, self).__init__(*args, **kwargs)
+        self.cache = {}
+
+    def make_map(self):
+        m = Mapper()
+        m.connect('', controller='getList')
+        m.connect(':action', controller='getUser')
+        return m
+
+    def getUser(self, action, **kwargs):
+        try:
+            if action not in self.cache:
+                self.cache[action] = pwd.getpwnam(action).pw_dir
+            return routefs.Symlink(self.cache[action])
+        except KeyError:
+            return
+
+    def getList(self, **kwargs):
+        return self.cache.keys()
+
+
+if __name__ == '__main__':
+    routefs.main(HomeFS)
index 92986a2..2a9bfb0 100755 (executable)
@@ -1,27 +1,31 @@
 #!/usr/bin/python
 
+
 import hesiod
-import routefs
 from routes import Mapper
 
+import routefs
+
+
 class PyHesiodFS(routefs.RouteFS):
+    controllers = ['getList', 'getReadme', 'getLocker']
     def __init__(self, *args, **kwargs):
         super(PyHesiodFS, self).__init__(*args, **kwargs)
         self.fuse_args.add("allow_other", True)
-        
+
         self.cache = {}
-    
+
     def make_map(self):
         m = Mapper()
         m.connect('', controller='getList')
         m.connect('README.txt', controller='getReadme')
         m.connect(':action', controller='getLocker')
         return m
-    
+
     def getLocker(self, action, **kwargs):
         if action in self.cache:
             return routefs.Symlink(self.cache[action])
-        
+
         try:
             filsys = hesiod.FilsysLookup(action).filsys[0]
             if filsys['type'] == 'AFS':
@@ -29,10 +33,10 @@ class PyHesiodFS(routefs.RouteFS):
                 return routefs.Symlink(self.cache[action])
         except (TypeError, KeyError, IndexError):
             return
-    
+
     def getList(self, **kwargs):
         return self.cache.keys() + ['README.txt']
-    
+
     def getReadme(self, **kwargs):
         return """
 This is the pyHesiodFS FUSE automounter. To access a Hesiod filsys,
@@ -42,5 +46,6 @@ If you're using the Finder, try pressing Cmd+Shift+G and then entering
 /mit/name
 """
 
+
 if __name__ == '__main__':
     routefs.main(PyHesiodFS)
index a0421c3..6012ddf 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -4,11 +4,11 @@ from setuptools import setup, find_packages
 
 setup(
     name="RouteFS",
-    version="1.0.1",
+    version="1.1.0",
     description="RouteFS: A FUSE API wrapper based on URL routing",
     author="Evan Broder",
     author_email="broder@mit.edu",
-    url="http://ebroder.net/code/RouteFS",
+    url="http://github.com/ebroder/python-routefs/wikis",
     license="MPL, GPL",
     packages=find_packages(),
     install_requires=['fuse_python>=0.2a', 'Routes>=1.7'],