Repository: ianpreston/redditfs Branch: master Commit: b49d82c21241 Files: 5 Total size: 8.5 KB Directory structure: gitextract_yk4e18h5/ ├── .gitignore ├── README.md ├── fsfile.py ├── redditfs.py └── reqs.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ venv *.pyc ================================================ FILE: README.md ================================================ # redditfs Everything is better in an 80x25 green-and-black terminal -- even Reddit! `redditfs` maps subreddits to a FUSE filesystem, so you can use your favorite shell (or GUI file browser, or SSH, or FTP) to browse Reddit. ## Example $ ls -l /r/programming total 0 dr-xr-xr-x@ 3 root wheel 0 Jan 16 10:30 ANN:_pandas_0130_released dr-xr-xr-x@ 3 root wheel 0 Jan 18 13:08 An_evaluation_of_simple_Python_performance_tweaks dr-xr-xr-x@ 3 root wheel 0 Jan 17 08:36 Anyone_have_experience_installing_Folium_for_Py_33? dr-xr-xr-x@ 3 root wheel 0 Jan 18 09:48 Are_there_any_python_made_games_on_Steam? dr-xr-xr-x@ 3 root wheel 0 Jan 16 20:06 Beginner:_Getting_Beyond_Syntax dr-xr-xr-x@ 3 root wheel 0 Jan 18 00:53 Best_Questions_to_ask_when_hiring_a_Python_dev? ... $ ls -l /r/programming/An_evaluation_of_simple_Python_performance_tweaks total 16 -r--r--r--@ 1 root wheel 97 Jan 18 13:08 permalink -r--r--r--@ 1 root wheel 0 Jan 18 13:08 selftext -r--r--r--@ 1 root wheel 72 Jan 18 13:08 url $ cat /r/programming/Best_Questions_to_ask_when_hiring_a_Python_dev?/selftext I'm a long time C/C++/C# dev who is now diving into python head on, and using it on a project here in Seattle. Part of this is I need to grow my team and hire ... $ lynx $(cat /r/programming/An_evaluation_of_simple_Python_performance_tweaks/url) ## Howto You'll need Python >= 2.7 or >= 3.4, and FUSE. You can install FUSE via the package managers on most Linux distros. On OSX, you can get FUSE support via [OSXFUSE](http://osxfuse.github.io/). $ git clone https://github.com/ianpreston/redditfs.git $ cd redditfs && virtualenv env && source env/bin/activate $ pip install -r reqs.txt $ mkdir /r $ python redditfs.py /r ## License Available under the MIT License. ================================================ FILE: fsfile.py ================================================ import stat import enum DirectoryType = enum.Enum('DirectoryType', 'root normal subreddit') class FSFile(object): BASE_MODE = stat.S_IFREG def __init__(self, filename, mode, content, ctime): self.filename = filename self._mode = FSFile.BASE_MODE | mode self._content = content.encode('ascii', errors='ignore') self._size = len(self._content) self._time = ctime def getattr(self): return { 'st_size': self._size, 'st_nlink': 1, 'st_ctime': self._time, 'st_mtime': self._time, 'st_atime': self._time, 'st_mode': self._mode, } def read(self, size, offset): return self._content[offset:offset+size] def dir(self): return False class FSDirectory(object): BASE_MODE = stat.S_IFDIR def __init__(self, filename, dirtype, mode, ctime): self.filename = filename self.dirtype = dirtype self._mode = FSDirectory.BASE_MODE | mode self._time = ctime self._children = {} def add_child(self, child): self._children[child.filename] = child def get_child(self, path): return self._children.get(path) def remove_child(self, path): del self._children[path] def getattr(self): return { 'st_size': 0, 'st_nlink': len(self._children), 'st_ctime': self._time, 'st_mtime': self._time, 'st_atime': self._time, 'st_mode': self._mode, } def readdir(self): return ['.', '..'] + list(self._children.keys()) def dir(self): return True ================================================ FILE: redditfs.py ================================================ import fuse import errno import stat import time import sys import requests import os import os.path from fsfile import * try: import urlparse except ImportError: import urllib.parse as urlparse CACHE_TIMEOUT = 60 * 60 class RedditFS(fuse.Operations): PERMS = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH DIR_PERMS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH def __init__(self): self.fd = 0 self.fs = FSDirectory( '/', DirectoryType.root, RedditFS.PERMS | RedditFS.DIR_PERMS, time.time(), ) @property def dirlist(self): if not self._dirlist: self._dirlist = self._populate_dirlist() return self._dirlist def open(self, path, flags): self.fd += 1 return self.fd def getattr(self, path, fh=None): f = self.traverse(path) if f is None: raise fuse.FuseOSError(errno.ENOENT) return f.getattr() def read(self, path, size, offset, fh): f = self.traverse(path) if f is None: raise fuse.FuseOSError(errno.ENOENT) if f.dir(): raise fuse.FuseOSError(errno.EISDIR) return f.read(size, offset) def readdir(self, path, fh): f = self.traverse(path) if f is None: raise fuse.FuseOSError(errno.ENOENT) if not f.dir(): raise fuse.FuseOSError(errno.ENOTDIR) return f.readdir() def traverse(self, path): path = self._split_path(path) node = self.fs return self._traverse(path, node) def _traverse(self, path, node): if len(path) == 0: return node fn = path.pop(0) next_node = node.get_child(fn) if node.dirtype == DirectoryType.root: self._lazy_load_subreddit(next_node, fn) return self._traverse(path, next_node) def _split_path(self, path): # TODO Move to a util module ? head, tail = os.path.split(path) if tail == '': return [] if head == '' or head == os.sep: return [tail] return self._split_path(head) + [tail] def _lazy_load_subreddit(self, node, filename): # If the directory does not exist, attempt to load it if node is None: return self._populate_subreddit(filename) # If the directory exists but was created more than CACHE_TIMEOUT # seconds ago, re-populate the directory if node.getattr().get('st_ctime') < (time.time() - CACHE_TIMEOUT): self.fs.remove_child(node.filename) return self._populate_subreddit(node.filename) # The directory exists and is fresh, return it return node def _populate_subreddit(self, subreddit): r = requests.get( 'http://api.reddit.com/r/{}/hot'.format(subreddit), headers={ 'User-Agent': 'redditfs /u/evilyomiel' }, allow_redirects=False, ) if r.status_code in [404, 302]: return r.raise_for_status() links = [link['data'] for link in r.json()['data']['children']] root_file = FSDirectory( filename=subreddit, dirtype=DirectoryType.subreddit, mode=RedditFS.PERMS | RedditFS.PERMS, ctime=time.time(), ) for zelda in links: self._add_reddit_link_to_fs(root_file, zelda) self.fs.add_child(root_file) return root_file def _add_reddit_link_to_fs(self, fs, zelda): title = zelda['title'] filename = self._sanitize_path(title) permalink = urlparse.urljoin( 'http://www.reddit.com/', zelda['permalink'] ) url = zelda['url'] selftext = zelda['selftext'] root_file = FSDirectory( filename=filename, dirtype=DirectoryType.normal, mode=RedditFS.PERMS | RedditFS.DIR_PERMS, ctime=zelda['created_utc'], ) permalink_file = FSFile( filename='permalink', mode=RedditFS.PERMS, content=permalink, ctime=zelda['created_utc'], ) url_file = FSFile( filename='url', mode=RedditFS.PERMS, content=url, ctime=zelda['created_utc'], ) selftext_file = FSFile( filename='selftext', mode=RedditFS.PERMS, content=selftext, ctime=zelda['created_utc'], ) for f in (permalink_file, url_file, selftext_file): root_file.add_child(f) fs.add_child(root_file) def _sanitize_path(self, path): replace = ( ('/', ''), (' ', '_'), ("'", ''), ('"', ''), ) for r in replace: path = path.replace(*r) return path.lower() def main(): fuse.FUSE(RedditFS(), sys.argv[1], foreground=True, nothreads=True) if __name__ == '__main__': main() ================================================ FILE: reqs.txt ================================================ enum34==1.0 fusepy==2.0.2 requests==2.2.0