Created
May 11, 2011 19:27
-
-
Save clayg/967119 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
""" | |
My take on resource CRUD mapped to RESTful url handling | |
""" | |
import errno | |
import json | |
import os | |
from optparse import OptionParser | |
import sys | |
import time | |
from threading import Thread | |
from SocketServer import ThreadingMixIn | |
from wsgiref.simple_server import WSGIServer, make_server | |
from webob.dec import wsgify | |
from webob import exc | |
from webob import Response | |
def background(f, *args, **kwargs): | |
# this should be removed | |
orig_f = f | |
def new_f(*args, **kwargs): | |
time.sleep(4) | |
return orig_f(*args, **kwargs) | |
f = new_f | |
Thread(target=f, args=args, kwargs=kwargs).start() | |
class MultiThreadWSGIServer(ThreadingMixIn, WSGIServer): | |
multithread = True | |
class Controller(object): | |
collection_action_map = { | |
'POST': 'create', | |
'GET': 'list', | |
} | |
resource_action_map = { | |
'PUT': 'create', | |
'GET': 'read', | |
'POST': 'update', | |
'DELETE': 'delete', | |
} | |
def __call__(self, req): | |
id = req.path_info_pop() | |
if not id: | |
action_map = self.collection_action_map | |
else: | |
remainder = req.path_info_pop() | |
if remainder: | |
return exc.HTTPBadRequest() | |
action_map = self.resource_action_map | |
try: | |
action = action_map[req.method] | |
except KeyError: | |
return exc.HTTPMethodNotAllowed() | |
try: | |
rv = getattr(self, action)(id=id, **req.params) | |
except exc.HTTPException: | |
raise | |
if isinstance(rv, exc.HTTPException): | |
return rv | |
elif rv is None: | |
return Response() | |
else: | |
return Response(json.dumps(rv) + '\n') | |
def list(self, **kwargs): | |
return exc.HTTPMethodNotAllowed() | |
def create(self, id, **kwargs): | |
return exc.HTTPMethodNotAllowed() | |
def read(self, id, **kwargs): | |
return exc.HTTPMethodNotAllowed() | |
def update(self, id, **kwargs): | |
return exc.HTTPMethodNotAllowed() | |
def delete(self, id, **kwargs): | |
return exc.HTTPMethodNotAllowed() | |
class Application(object): | |
@wsgify | |
def __call__(self, req): | |
rtype = req.path_info_pop().lower() | |
try: | |
controller = getattr(self, rtype) | |
except AttributeError: | |
time.sleep(int(req.params.get('sleep', 0))) | |
return exc.HTTPNotFound() | |
return controller(self)(req) | |
class ObjectController(Controller): | |
def __init__(self, app): | |
self.app = app | |
def list(self, name=None, **kwargs): | |
objs = os.listdir(self.app.path) | |
if name: | |
objs = [x for x in objs if x == name] | |
return objs | |
def _get_path(self, id=''): | |
return os.path.join(self.app.path, id) | |
def create(self, id, **kwargs): | |
if not id: | |
raise exc.HTTPBadRequest() | |
kwargs['name'] = kwargs.get('name', id) | |
kwargs['status'] = kwargs.get('status', 'NEW') | |
with open(self._get_path(id), 'w') as f: | |
f.write(json.dumps(kwargs)) | |
# start a background job before returning | |
background(self.update, id, status='ACTIVE') | |
return kwargs | |
def update(self, id, **kwargs): | |
with open(self._get_path(id)) as f: | |
merged_kwargs = json.loads(f.read()) | |
merged_kwargs.update(kwargs) | |
with open(self._get_path(id), 'w') as f: | |
f.write(json.dumps(merged_kwargs)) | |
return merged_kwargs | |
def read(self, id, **kwargs): | |
try: | |
with open(self._get_path(id)) as f: | |
return json.loads(f.read()) | |
except IOError, e: | |
if e.errno == errno.ENOENT: | |
raise exc.HTTPNotFound() | |
raise | |
def delete(self, id, **kwargs): | |
try: | |
os.remove(self._get_path(id)) | |
except OSError, e: | |
if e.errno != errno.ENOENT: | |
raise | |
class MyApp(Application): | |
object = ObjectController | |
def __init__(self, path): | |
self.path = path | |
def main(): | |
parser = OptionParser('%prog [options]\n' + __doc__.rstrip()) | |
parser.add_option('-p', '--port', type="int", default=8080, | |
help="set port for server to listen on") | |
parser.add_option('-d', '--dir', default='./web', | |
help="set dir to use for content") | |
options, args = parser.parse_args() | |
path = os.path.abspath(options.dir) | |
httpd = make_server('', options.port, MyApp(path), | |
server_class=MultiThreadWSGIServer) | |
print "Serving objects from %s on port %s..." % (path, options.port) | |
try: | |
httpd.serve_forever() | |
except KeyboardInterrupt: | |
print 'user quit' | |
if __name__ == "__main__": | |
sys.exit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment