And the re-designed version 0.0.6 is out on PyPI. More detailed docs on github: https://github.com/jacek99/corepost
Cheers, Jacek On Mon, Sep 5, 2011 at 1:41 PM, Jacek Furmankiewicz <[email protected]>wrote: > Hi Ian, I think the problem though is that your solution relies on static > class-level methods and variables, whereas I need more instance-level. > > Here's my solution (seems to be working so far) > > 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 > > and then the following method is called from the instance constructor: > > 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 > > I am updating docs and examples, 0.0.6 should be out soon with these > changes, > here's what the API looks like now with these changes: > > class HomeApp(CorePost): > > @route("/") > def home_root(self,request,**kwargs): > return "HOME %s" % kwargs > > class Module1(CorePost): > > @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 > > 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('', HomeApp()) > app.putChild('module1',Module1()) > app.putChild('module2',Module2()) > > factory = Site(app) > reactor.listenTCP(8081, factory) > reactor.run() > > Jacek > > > On Mon, Sep 5, 2011 at 1:25 PM, Ian Rees <[email protected]> wrote: > >> Actually, it turns out our solution was a little more complicated :P Here >> is our basic implementation (our actual class is a bit more involved and >> allows multiple routes per method, regular expressions, etc..). The view's >> method decorators are evaluated before the view's class decorator. The class >> decorator looks at all the methods and sees which have been decorated with >> the route. >> >> class Routing(object): >> routes = {} >> >> @classmethod >> def register(cls, viewclass): >> # Check a class for any methods that have routes as >> attributes >> # and add these to the routing dictionary >> for k,v in viewclass.__dict__.items(): >> if hasattr(v, 'route'): >> print "Registering %s method %s with route >> %s"%(viewclass, v.__name__, v.route) >> cls.routes[v.route] = (viewclass, >> v.__name__) >> return cls >> >> @classmethod >> def addroute(cls, route): >> # This decorator adds the specified routes as attributes >> to the methods. >> # These routes will be found when the view class is >> registered. >> def wrap(handler): >> handler.route = route >> return handler >> return wrap >> >> @classmethod >> def resolve(cls, route): >> for r in cls.routes: >> if r == route: >> print "Found handler for %s:"%route, >> cls.routes[r] >> view, method = cls.routes[r] >> inst = view() >> return getattr(inst, method) >> >> >> @Routing.register >> class View(object): >> @Routing.addroute(r'/blog/post/') >> def dosomething(self, title=None, body=None): >> print "Adding blog post:", title, body >> >> >> handler = Routing.resolve('/blog/post/') >> handler(title="Test", body="ok") >> >> >> >> Thanks, >> Ian >> >> On Sep 5, 2011, at 11:14 AM, Jacek Furmankiewicz wrote: >> >> > Hi Glyph, >> > >> > I looked at your suggestion, but unfortunately the implementation is >> very complex, if not impossible. >> > >> > The main problem is that >> > a) a class method with a decorator "forgets" its class, so it's >> impossible from the decorator which class it belongs to. >> > The function has not been bound to a class yet when the decorator is >> called for the first time, so there is no way for it to notify the >> containing class that this function defines a route for it >> > >> > b) is is next to impossible for a class to scan its own function and >> find their decorators. I've seen some hacks on StackOverflow >> > where it actually parses the source code, but that is an ugly hack to >> say the least (and probably prone to many bugs) >> > >> > In general, it seems decorators on class methods are missing such >> crucial functionality as finding out which class the method belongs to. >> > Sort of a key requirement, if you ask me (at least after lots of >> experience with Java or .Net reflection, where getting this sort of info is >> trivial). >> > >> > if you have any suggestions on how to accomplish your recommendation, I >> would greatly appreciate it. >> > >> > The decorator in question that I would need to take out of the CorePost >> class and make it a standalone function looks like this: >> > >> > 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 >> > >> > it's obtaining the reference to 'self' when it is not a class method any >> more is the problem. Not sure how to get around it. >> > >> > Cheers, >> > Jacek >> > >> > On Sun, Sep 4, 2011 at 12:01 AM, Glyph Lefkowitz < >> [email protected]> wrote: >> > >> > On Sep 3, 2011, at 8:28 PM, Jacek Furmankiewicz wrote: >> > >> >> Any feedback is welcome >> > >> > Hi Jacek, >> > >> > Great to see more development going into Twisted-based web stuff! :) >> > >> > However, I do have one question. Maybe I'm missing something about the >> way Flask does things, but it seems very odd to me that the decorators >> you're using are applied to global functions, rather than instances of an >> object. For example, instead of: >> > >> > app = CorePost() >> > ... >> > @app.route("/validate/<int:rootId>/schema",Http.POST) >> > @validate(schema=TestSchema) >> > def postValidateSchema(request,rootId,childId,**kwargs): >> > '''Validate using a common schema''' >> > return "%s - %s - %s" % (rootId,childId,kwargs) >> > >> > You could do: >> > >> > class MyPost(CorePost): >> > @route("/validate/<int:rootId>/schema",Http.POST) >> > @validate(schema=TestSchema) >> > def postValidateSchema(self,request,rootId,childId,**kwargs): >> > '''Validate using a common schema''' >> > return "%s - %s - %s" % (rootId,childId,kwargs) >> > >> > This would allow for re-usable objects; for example, rather than having >> a "blog article create" API (sorry for the uninspired example, it's late) >> for your entire site, you would have a "article create" API on a "Blog", >> which would enable you to have multiple Blog objects (perhaps with different >> authors, in different permission domains, etc). This would also make >> re-using the relevant objects between different applications easier. >> > >> > In other words, global variables are bad, and this looks like it depends >> rather heavily on them. >> > >> > Any thoughts on this? Am I missing the point? >> > >> > Thanks, >> > >> > -glyph >> > >> > >> > _______________________________________________ >> > Twisted-web mailing list >> > [email protected] >> > http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web >> > >> > >> > _______________________________________________ >> > Twisted-web mailing list >> > [email protected] >> > http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web >> >> >> _______________________________________________ >> Twisted-web mailing list >> [email protected] >> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web >> > >
_______________________________________________ Twisted-web mailing list [email protected] http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
