Permalink
Browse files

Reorganize module structure

  • Loading branch information...
1 parent c66928c commit d29ad797d806a3888ed996eb49d99c5de60a982d @alekstorm committed Sep 16, 2011
Showing with 227 additions and 227 deletions.
  1. +2 −0 .gitignore
  2. +3 −2 test/test.py
  3. +214 −1 vortex/__init__.py
  4. +0 −150 vortex/app.py
  5. +7 −44 vortex/{resource.py → resources.py}
  6. +1 −30 vortex/{response.py → responses.py}
View
@@ -1,3 +1,5 @@
+*.pyc
+
build/
dist/
*.egg-info/
View
@@ -3,10 +3,11 @@
import time
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
+import uuid
-from vortex.app import *
+from vortex import Resource
from vortex.memcached import Memcacher, memcached
-from vortex.resource import *
+from vortex.resources import *
logging.getLogger('vortex').addHandler(logging.StreamHandler())
View
@@ -1 +1,214 @@
-__all__ = ['app', 'memcached', 'resource', 'response']
+import Cookie
+import hashlib
+import httplib
+import json
+import logging
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+from tornado.escape import utf8
+import tornado.web
+import traceback
+import urllib
+from xml.etree.ElementTree import Element, ElementTree, iselement
+
+logger = logging.getLogger('vortex')
+
+SAFE_METHODS = ('GET', 'HEAD')
+
+def json2xml(data):
+ def convert_elem(data, tag='item'):
+ root = Element(tag)
+ if isinstance(data, dict):
+ for key, val in data.iteritems():
+ if isinstance(val, dict) or isinstance(val, list):
+ root.append(convert_elem(val, key))
+ else:
+ root.set(key, str(val))
+ elif isinstance(data, list):
+ for item in data:
+ root.append(convert_elem(item))
+ else:
+ root.set('_value', str(data))
+ return root
+
+ return lambda request: convert_elem(data, 'root')
+
+def format(handlers, default=None, unknown=None):
+ def wrap1(getitem):
+ def wrap2(self, name):
+ parts = name.split('.')
+ resource = getitem(self, parts[0])
+ if len(parts) > 1:
+ handler = handlers.get(parts[1], None)
+ if handler:
+ return handler(resource)
+ if unknown:
+ return unknown(resource)
+ raise KeyError()
+ if default:
+ return default(resource)
+ raise KeyError()
+ return wrap2
+ return wrap1
+
+def authenticate(retrieve, cookie_name, redirect=None, unauthorized=None):
+ def wrap1(fn):
+ def wrap2(self, request, *args, **kwargs):
+ cookie = request.cookies.get(cookie_name, None)
+ if cookie is not None:
+ user = retrieve(cookie)
+ if user is not None:
+ return fn(self, request, user, *args, **kwargs)
+ if redirect and request.method in SAFE_METHODS:
+ return HTTPResponse(httplib.FOUND, headers={'Location': redirect})
+ return unauthorized(request) if unauthorized else HTTPUnauthorizedResponse()
+ return wrap2
+ return wrap1
+
+def xsrf(cookie):
+ def wrap1(fn):
+ def wrap2(self, request, *args, **kwargs):
+ if request.method not in SAFE_METHODS and ('_xsrf' not in kwargs or cookie not in request.cookies or kwargs.pop('_xsrf') != request.cookies[cookie].value):
+ return HTTPResponse(httplib.FORBIDDEN, entity='XSRF cookie does not match request argument')
+ return fn(self, request, *args, **kwargs)
+ return wrap2
+ return wrap1
+
+def signed_cookie(secret):
+ def wrap1(fn):
+ def wrap2(self, request, *args, **kwargs):
+ for key, cookie in request.cookies.items():
+ value = tornado.web.decode_signed_value(secret, cookie.key, cookie.value)
+ if value is None:
+ del request.cookies[key]
+ else:
+ cookie.set(key, value, value)
+ response = fn(self, request)
+ for key, cookie in getattr(response, 'cookies', {}).iteritems():
+ value = tornado.web.create_signed_value(secret, key, unicode(cookie.value))
+ cookie.set(key, value, value)
+ cookie['path'] = '/'
+ return response
+ return wrap2
+ return wrap1
+
+def coerce_response(response):
+ if response is None:
+ response = HTTPResponse(httplib.NO_CONTENT)
+ elif isinstance(response, basestring):
+ response = HTTPResponse(entity=response)
+ elif isinstance(response, dict):
+ response = HTTPResponse(entity=json.dumps(response), headers={'Content-Type': 'application/json'})
+ elif iselement(response):
+ xml = StringIO.StringIO()
+ ElementTree(response).write(xml)
+ response = HTTPResponse(entity=xml.getvalue(), headers={'Content-Type': 'application/xml'})
+ return response
+
+
+class Resource(object):
+ SUPPORTED_METHODS = set(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE'])
+
+ def __call__(self, request, *args):
+ method_name = request.method.lower()
+ try:
+ method = getattr(self, method_name) # TODO check in SUPPORTED_METHODS
+ except AttributeError:
+ if not hasattr(self, method_name):
+ get_method = getattr(self, 'get', None)
+ if request.method == 'HEAD' and get_method is not None:
+ method = get_method
+ elif request.method.upper() in self.SUPPORTED_METHODS:
+ return HTTPResponse(httplib.METHOD_NOT_ALLOWED, headers={'Allowed': [method for method in self.SUPPORTED_METHODS if hasattr(self, method.lower())]})
+ else:
+ return HTTPResponse(httplib.NOT_IMPLEMENTED)
+ else:
+ raise
+
+ kwargs = dict([(key, value[0]) for key, value in request.arguments.iteritems()])
+ try:
+ return method(request, *args, **kwargs)
+ except TypeError as err:
+ argspec = inspect.getargspec(method)
+ args = argspec.args[2:]
+ keywords = set(kwargs.keys())
+ missing = set(args[:-len(argspec.defaults)] if argspec.defaults else args) - keywords
+ if len(missing) > 0:
+ return HTTPResponse(httplib.BAD_REQUEST, entity='Missing arguments: '+' '.join(missing))
+ invalid = keywords - set(args)
+ if not argspec.keywords and len(invalid) > 0:
+ return HTTPResponse(httplib.BAD_REQUEST, entity='Unexpected arguments: '+' '.join(invalid))
+ raise
+
+
+class HTTPResponse(object):
+ def __init__(self, status_code=httplib.OK, reason=None, entity='', version='HTTP/1.1', headers=None, cookies=None):
+ self.status_code = status_code
+ self.reason = reason
+ self.entity = entity
+ self.version = version
+ self.headers = headers or {}
+ self.cookies = Cookie.SimpleCookie()
+ for key, value in (cookies or {}).iteritems():
+ if isinstance(value, dict):
+ self.cookies[key] = value['value']
+ for name, morsel_attr in value:
+ self.cookies[key][name] = morsel_attr
+ else:
+ self.cookies[key] = value
+
+ def __str__(self):
+ lines = [utf8(self.version + b' ' + str(self.status_code) + b' ' + (self.reason or httplib.responses[self.status_code]))]
+ self.headers.setdefault('Content-Length', str(len(self.entity)))
+ self.headers.setdefault('Content-Type', 'text/html')
+ for name, values in self.headers.iteritems():
+ lines.extend([utf8(name) + b': ' + utf8(value) for value in (values if isinstance(values, list) else [values])])
+ for cookie in self.cookies.itervalues():
+ lines.append(str(cookie))
+ return b'\r\n'.join(lines) + b'\r\n\r\n' + self.entity
+
+
+class Application(object):
+ def __init__(self, root=None):
+ self.root = root
+
+ def __call__(self, request):
+ try:
+ resource = self.root
+ response = None
+ for part in request.path.split('/')[1:]:
+ not_found = False
+ if resource is not None and hasattr(resource, '__getitem__'):
+ try:
+ resource = resource[urllib.unquote(part)]
+ except KeyError:
+ not_found = True
+ else:
+ not_found = True
+ if not_found:
+ response = HTTPResponse(httplib.NOT_FOUND)
+ break
+ if response is None:
+ response = coerce_response(resource(request)) if hasattr(resource, '__call__') else HTTPResponse(httplib.METHOD_NOT_ALLOWED)
+
+ if response.status_code == httplib.OK and request.method in SAFE_METHODS:
+ etag = hashlib.sha1(response.entity).hexdigest()
+ inm = request.headers.get('If-None-Match')
+ if inm and inm.find(etag) != -1:
+ response = HTTPResponse(httplib.NOT_MODIFIED)
+ else:
+ response.headers.setdefault('Etag', etag)
+
+ if request.method == 'HEAD':
+ response.entity = ''
+ except:
+ response = HTTPResponse(httplib.INTERNAL_SERVER_ERROR, entity=traceback.format_exc())
+ if response.status_code >= 500: # TODO print request
+ logger.error(str(response))
+ elif response.status_code >= 400:
+ logger.warning(str(response))
+ request.write(str(response))
+ request.finish()
+ return response
View
@@ -1,150 +0,0 @@
-import hashlib
-import httplib
-import json
-import logging
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-import tornado.web
-import traceback
-import urllib
-from xml.etree.ElementTree import Element, ElementTree, iselement
-
-from vortex.response import *
-
-logger = logging.getLogger('vortex')
-
-def json2xml(data):
- def convert_elem(data, tag='item'):
- root = Element(tag)
- if isinstance(data, dict):
- for key, val in data.iteritems():
- if isinstance(val, dict) or isinstance(val, list):
- root.append(convert_elem(val, key))
- else:
- root.set(key, str(val))
- elif isinstance(data, list):
- for item in data:
- root.append(convert_elem(item))
- else:
- root.set('_value', str(data))
- return root
-
- return lambda request: convert_elem(data, 'root')
-
-def format(handlers, default=None, unknown=None):
- def wrap1(getitem):
- def wrap2(self, name):
- parts = name.split('.')
- resource = getitem(self, parts[0])
- if len(parts) > 1:
- handler = handlers.get(parts[1], None)
- if handler:
- return handler(resource)
- if unknown:
- return unknown(resource)
- raise KeyError()
- if default:
- return default(resource)
- raise KeyError()
- return wrap2
- return wrap1
-
-def authenticate(retrieve, cookie_name, redirect=None, unauthorized=None):
- def wrap1(fn):
- def wrap2(self, request, *args, **kwargs):
- cookie = request.cookies.get(cookie_name, None)
- if cookie is not None:
- user = retrieve(cookie)
- if user is not None:
- return fn(self, request, user, *args, **kwargs)
- if redirect and request.method in SAFE_METHODS:
- return HTTPFoundResponse(redirect)
- return unauthorized(request) if unauthorized else HTTPUnauthorizedResponse()
- return wrap2
- return wrap1
-
-def xsrf(cookie):
- def wrap1(fn):
- def wrap2(self, request, *args, **kwargs):
- if request.method not in SAFE_METHODS and ('_xsrf' not in kwargs or cookie not in request.cookies or kwargs.pop('_xsrf') != request.cookies[cookie].value):
- return HTTPForbiddenResponse(entity='XSRF cookie does not match request argument')
- return fn(self, request, *args, **kwargs)
- return wrap2
- return wrap1
-
-def signed_cookie(secret):
- def wrap1(fn):
- def wrap2(self, request, *args, **kwargs):
- for key, cookie in request.cookies.items():
- value = tornado.web.decode_signed_value(secret, cookie.key, cookie.value)
- if value is None:
- del request.cookies[key]
- else:
- cookie.set(key, value, value)
- response = fn(self, request)
- for key, cookie in getattr(response, 'cookies', {}).iteritems():
- value = tornado.web.create_signed_value(secret, key, unicode(cookie.value))
- cookie.set(key, value, value)
- cookie['path'] = '/'
- return response
- return wrap2
- return wrap1
-
-def coerce_response(response):
- if response is None:
- response = HTTPNoContentResponse()
- elif isinstance(response, basestring):
- response = HTTPResponse(entity=response)
- elif isinstance(response, dict):
- response = HTTPResponse(entity=json.dumps(response), headers={'Content-Type': 'application/json'})
- elif iselement(response):
- xml = StringIO.StringIO()
- ElementTree(response).write(xml)
- response = HTTPResponse(entity=xml.getvalue(), headers={'Content-Type': 'application/xml'})
- return response
-
-
-class Application(object):
- def __init__(self, root=None):
- self.root = root
-
- def __call__(self, request):
- try:
- resource = self.root
- response = None
- for part in request.path.split('/')[1:]:
- not_found = False
- if resource is not None and hasattr(resource, '__getitem__'):
- try:
- resource = resource[urllib.unquote(part)]
- except KeyError:
- not_found = True
- else:
- not_found = True
- if not_found:
- response = HTTPNotFoundResponse()
- break
- if response is None:
- response = coerce_response(resource(request)) if hasattr(resource, '__call__') else HTTPMethodNotAllowedResponse(allowed=[])
-
- if response.status_code == httplib.OK and request.method in SAFE_METHODS:
- etag = hashlib.sha1(response.entity).hexdigest()
- inm = request.headers.get('If-None-Match')
- if inm and inm.find(etag) != -1:
- response = HTTPNotModifiedResponse()
- else:
- response.headers.setdefault('Etag', etag)
-
- if request.method == 'HEAD':
- response.entity = ''
- except:
- response = HTTPInternalServerErrorResponse(entity=traceback.format_exc())
- if response.status_code >= 500: # TODO print request
- logger.error(str(response))
- elif response.status_code >= 400:
- logger.warning(str(response))
- request.write(str(response))
- request.finish()
- return response
Oops, something went wrong.

0 comments on commit d29ad79

Please sign in to comment.