Commit 446c73bf authored by Jacek Furmankiewicz's avatar Jacek Furmankiewicz

0.0.6: reworked API to be class/method oriented and avoid global objects

parent fa5d53b9
......@@ -3,30 +3,31 @@ Argument extraction tests
@author: jacekf
'''
from corepost.web import CorePost, validate
from corepost.web import CorePost, validate, route
from corepost.enums import Http
from formencode import Schema, validators
app = CorePost()
class TestSchema(Schema):
allow_extra_fields = True
childId = validators.Regex(regex="^jacekf|test$")
@app.route("/int/<int:intarg>/float/<float:floatarg>/string/<stringarg>",Http.GET)
def test(request,intarg,floatarg,stringarg,**kwargs):
args = (intarg,floatarg,stringarg)
return "%s" % map(lambda x: (type(x),x),args)
@app.route("/validate/<int:rootId>/schema",Http.POST)
@validate(schema=TestSchema)
def postValidateSchema(request,rootId,childId,**kwargs):
return "%s - %s - %s" % (rootId,childId,kwargs)
@app.route("/validate/<int:rootId>/custom",Http.POST)
@validate(childId=validators.Regex(regex="^jacekf|test$"))
def postValidateCustom(request,rootId,childId,**kwargs):
return "%s - %s - %s" % (rootId,childId,kwargs)
class ArgumentApp(CorePost):
@route("/int/<int:intarg>/float/<float:floatarg>/string/<stringarg>",Http.GET)
def test(self,request,intarg,floatarg,stringarg,**kwargs):
args = (intarg,floatarg,stringarg)
return "%s" % map(lambda x: (type(x),x),args)
@route("/validate/<int:rootId>/schema",Http.POST)
@validate(schema=TestSchema())
def postValidateSchema(self,request,rootId,childId,**kwargs):
return "%s - %s - %s" % (rootId,childId,kwargs)
@route("/validate/<int:rootId>/custom",Http.POST)
@validate(childId=validators.Regex(regex="^jacekf|test$"))
def postValidateCustom(self,request,rootId,childId,**kwargs):
return "%s - %s - %s" % (rootId,childId,kwargs)
def run_app_arguments():
app = ArgumentApp()
app.run(8082)
\ No newline at end of file
......@@ -3,42 +3,46 @@ Server tests
@author: jacekf
'''
from corepost.web import CorePost
from corepost.web import CorePost, route
from corepost.enums import Http
from twisted.internet import defer
app = CorePost()
@app.route("/",Http.GET)
@defer.inlineCallbacks
def root(request,**kwargs):
yield 1
request.write("%s" % kwargs)
request.finish()
@app.route("/test",Http.GET)
def test(request,**kwargs):
return "%s" % kwargs
@app.route("/test/<int:numericid>/resource/<stringid>",Http.GET)
def test_get_resources(request,numericid,stringid,**kwargs):
return "%s - %s" % (numericid,stringid)
@app.route("/post",(Http.POST,Http.PUT))
def test_post(request,**kwargs):
return "%s" % kwargs
@app.route("/put",(Http.POST,Http.PUT))
def test_put(request,**kwargs):
return "%s" % kwargs
@app.route("/postput",(Http.POST,Http.PUT))
def test_postput(request,**kwargs):
return "%s" % kwargs
@app.route("/delete",Http.DELETE)
def test_delete(request,**kwargs):
return "%s" % kwargs
class HomeApp(CorePost):
@route("/",Http.GET)
@defer.inlineCallbacks
def root(self,request,**kwargs):
yield 1
request.write("%s" % kwargs)
request.finish()
@route("/test",Http.GET)
def test(self,request,**kwargs):
return "%s" % kwargs
@route("/test/<int:numericid>/resource/<stringid>",Http.GET)
def test_get_resources(self,request,numericid,stringid,**kwargs):
return "%s - %s" % (numericid,stringid)
@route("/post",(Http.POST,Http.PUT))
def test_post(self,request,**kwargs):
return "%s" % kwargs
@route("/put",(Http.POST,Http.PUT))
def test_put(self,request,**kwargs):
return "%s" % kwargs
@route("/postput",(Http.POST,Http.PUT))
def test_postput(self,request,**kwargs):
return "%s" % kwargs
@route("/delete",Http.DELETE)
def test_delete(self,request,**kwargs):
return "%s" % kwargs
def run_app_home():
app.run()
\ No newline at end of file
app = HomeApp()
app.run()
if __name__ == "__main__":
run_app_home()
\ No newline at end of file
'''
Created on 2011-09-02
Misc tests
@author: jacekf
'''
def dec(f):
print "DEC"
def wrap():
print "WRAP"
v = f()
return v
return wrap
@dec
def test():
print "TEST3232"
if __name__ == "__main__":
test()
test()
test()
\ No newline at end of file
......@@ -2,43 +2,43 @@
A CorePost module1 that can be merged into the main CorePost Resource
'''
from corepost.web import CorePost
from corepost.web import CorePost, route
from corepost.enums import Http
from twisted.web.resource import Resource
from twisted.internet import reactor
from twisted.web.server import Site
home = CorePost()
class HomeApp(CorePost):
@home.route("/")
def home_root(request,**kwargs):
return "HOME %s" % kwargs
@route("/")
def home_root(self,request,**kwargs):
return "HOME %s" % kwargs
module1 = CorePost('module1')
class Module1(CorePost):
@module1.route("/",Http.GET)
def module1_get(request,**kwargs):
return request.path
@route("/",Http.GET)
def module1_get(self,request,**kwargs):
return request.path
@route("/sub",Http.GET)
def module1e_sub(self,request,**kwargs):
return request.path
@module1.route("/sub",Http.GET)
def module1e_sub(request,**kwargs):
return request.path
module2 = CorePost('module2')
@module2.route("/",Http.GET)
def module2_get(request,**kwargs):
return request.path
@module2.route("/sub",Http.GET)
def module2_sub(request,**kwargs):
return request.path
class Module2(CorePost):
@route("/",Http.GET)
def module2_get(self,request,**kwargs):
return request.path
@route("/sub",Http.GET)
def module2_sub(self,request,**kwargs):
return request.path
def run_app_multi():
app = Resource()
app.putChild(home.path, home)
app.putChild(module1.path,module1)
app.putChild(module2.path,module2)
app.putChild('', HomeApp())
app.putChild('module1',Module1())
app.putChild('module2',Module2())
factory = Site(app)
reactor.listenTCP(8081, factory) #@UndefinedVariable
......
......@@ -3,16 +3,18 @@ Main server classes
@author: jacekf
'''
import re, copy, exceptions
from twisted.internet import reactor, defer
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
from twisted.web.http import parse_qs
from collections import defaultdict
from enums import MediaType
from corepost.enums import Http
from corepost.utils import getMandatoryArgumentNames
from enums import MediaType
from formencode import FancyValidator, Invalid
from twisted.internet import reactor, defer
from twisted.web.http import parse_qs
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
import re
import copy
import exceptions
class RequestRouter:
''' Common class for containing info related to routing a request to a function '''
......@@ -21,17 +23,16 @@ class RequestRouter:
__urlRegexReplace = {"":r"(?P<arg>.+)","int":r"(?P<arg>\d+)","float":r"(?P<arg>\d+.?\d*)"}
__typeConverters = {"int":int,"float":float}
def __init__(self,f,url,method,accepts,produces,cache):
def __init__(self,f,url,methods,accepts,produces,cache):
self.__url = url
self.__method = method
self.__methods = methods if isinstance(methods,tuple) else (methods,)
self.__accepts = accepts
self.__produces = produces
self.__cache = cache
self.__f = f
self.__argConverters = {} # dict of arg names -> group index
self.__schema = None
self.__validators = {}
self.__mandatory = getMandatoryArgumentNames(f)[1:]
self.__mandatory = getMandatoryArgumentNames(f)[2:]
#parse URL into regex used for matching
m = RequestRouter.__urlMatcher.findall(url)
......@@ -57,10 +58,13 @@ class RequestRouter:
return self.__cache
@property
def schema(self):
''''Returns the formencode Schema, if this URL uses custom validation schema'''
return self.__schema
def methods(self):
return self.__methods
@property
def url(self):
return self.__url
def addValidator(self,fieldName,validator):
'''Adds additional field-specific formencode validators'''
self.__validators[fieldName] = validator
......@@ -83,12 +87,12 @@ class RequestRouter:
else:
return None
def call(self,request,**kwargs):
def call(self,instance,request,**kwargs):
'''Forwards call to underlying method'''
for arg in self.__mandatory:
if arg not in kwargs:
raise TypeError("Missing mandatory argument '%s'" % arg)
return self.__f(request,**kwargs)
return self.__f(instance,request,**kwargs)
class CachedUrl():
'''
......@@ -113,7 +117,7 @@ class CorePost(Resource):
'''
isLeaf = True
def __init__(self,path='',schema=None):
def __init__(self,schema=None):
'''
Constructor
'''
......@@ -122,13 +126,22 @@ class CorePost(Resource):
self.__cachedUrls = defaultdict(dict)
self.__methods = {}
self.__routers = {}
self.__path = path
self.__schema = schema
self.__registerRouters()
@property
def path(self):
return self.__path
def __registerRouters(self):
from types import FunctionType
for _,func in self.__class__.__dict__.iteritems():
if type(func) == FunctionType and hasattr(func,'corepostRequestRouter'):
rq = func.corepostRequestRouter
for method in rq.methods:
self.__urls[method][rq.url] = rq
self.__routers[func] = rq # needed so that we can lookup the router for a specific function
def __registerFunction(self,f,url,methods,accepts,produces,cache):
if f not in self.__methods.values():
if not isinstance(methods,(list,tuple)):
......@@ -142,11 +155,8 @@ class CorePost(Resource):
self.__methods[url] = f
def route(self,url,methods=(Http.GET,),accepts=MediaType.WILDCARD,produces=None,cache=True):
"""Main decorator for registering REST functions """
def wrap(f,*args,**kwargs):
self.__registerFunction(f, url, methods, accepts, produces,cache)
return f
return wrap
'''Obsolete'''
raise RuntimeError("Do not @app.route() any more, as of 0.0.6 API has been re-designed around class methods, see docs and examples")
def render_GET(self,request):
""" Handles all GET requests """
......@@ -202,7 +212,7 @@ class CorePost(Resource):
#handle Deferreds natively
try:
val = urlrouter.call(request,**allargs)
val = urlrouter.call(self,request,**allargs)
if isinstance(val,defer.Deferred):
# we assume the method will call request.finish()
......@@ -243,6 +253,20 @@ class CorePost(Resource):
# DECORATORS
#
##################################################################################################
def route(url,methods=(Http.GET,),accepts=MediaType.WILDCARD,produces=None,cache=True):
'''
Main decorator for registering REST functions
'''
def decorator(f):
def wrap(*args,**kwargs):
return f
router = RequestRouter(f, url, methods, accepts, produces, cache)
setattr(wrap,'corepostRequestRouter',router)
return wrap
return decorator
def validate(schema=None,**vKwargs):
'''
......
......@@ -40,6 +40,7 @@ Links
Changelog
`````````
* 0.0.6 - redesigned API around classes and methods, rather than functions and global objects (after feedback from Twisted devs)
* 0.0.5 - added FormEncode validation for arguments
* 0.0.4 - path argument extraction, mandatory argument error checking
......@@ -57,7 +58,7 @@ def read(fname):
setup(
name="CorePost",
version="0.0.5",
version="0.0.6",
author="Jacek Furmankiewicz",
author_email="jacek99@gmail.com",
description=("A Twisted Web REST micro-framework"),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment