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.<div><br></div><div>Here&#39;s my solution (seems to be working so far)<br><br>
</div><div><div>def route(url,methods=(Http.GET,),accepts=MediaType.WILDCARD,produces=None,cache=True):</div><div>    &#39;&#39;&#39;</div><div>    Main decorator for registering REST functions</div><div>    &#39;&#39;&#39;</div>
<div>    def decorator(f):</div><div>        def wrap(*args,**kwargs):</div><div>            return f</div><div>        router = RequestRouter(f, url, methods, accepts, produces, cache)</div><div>        setattr(wrap,&#39;corepostRequestRouter&#39;,router)</div>
<div>        </div><div>        return wrap</div><div>    return decorator</div></div><div><br></div><div>and then the following method is called from the instance constructor:</div><div><br></div><div><div>   def __registerRouters(self):</div>
<div>        from types import FunctionType</div><div>        for _,func in self.__class__.__dict__.iteritems():</div><div>            if type(func) == FunctionType and hasattr(func,&#39;corepostRequestRouter&#39;):</div>
<div>                rq = func.corepostRequestRouter</div><div>                for method in rq.methods:</div><div>                    self.__urls[method][rq.url] = rq</div><div>                    self.__routers[func] = rq # needed so that we can lookup the router for a specific function</div>
</div><div><br></div><div>I am updating docs and examples, 0.0.6 should be out soon with these changes, </div><div>here&#39;s what the API looks like now with these changes:</div><div><br></div><div><div>class HomeApp(CorePost):</div>
<div><br></div><div>    @route(&quot;/&quot;)</div><div>    def home_root(self,request,**kwargs):</div><div>        return &quot;HOME %s&quot; % kwargs</div><div><br></div><div>class Module1(CorePost):</div><div><br></div>
<div>    @route(&quot;/&quot;,Http.GET)</div><div>    def module1_get(self,request,**kwargs):</div><div>        return request.path</div><div>    </div><div>    @route(&quot;/sub&quot;,Http.GET)</div><div>    def module1e_sub(self,request,**kwargs):</div>
<div>        return request.path</div><div><br></div><div>class Module2(CorePost):</div><div>    </div><div>    @route(&quot;/&quot;,Http.GET)</div><div>    def module2_get(self,request,**kwargs):</div><div>        return request.path</div>
<div>    </div><div>    @route(&quot;/sub&quot;,Http.GET)</div><div>    def module2_sub(self,request,**kwargs):</div><div>        return request.path</div><div><br></div><div>def run_app_multi():</div><div>    app = Resource()</div>
<div>    app.putChild(&#39;&#39;, HomeApp())</div><div>    app.putChild(&#39;module1&#39;,Module1())</div><div>    app.putChild(&#39;module2&#39;,Module2())</div><div><br></div><div>    factory = Site(app)</div><div>    reactor.listenTCP(8081, factory)  </div>
<div>    reactor.run()                   </div></div><div><br></div><div>Jacek</div><div><br></div><div><br><div class="gmail_quote">On Mon, Sep 5, 2011 at 1:25 PM, Ian Rees <span dir="ltr">&lt;<a href="mailto:ian@ianrees.net">ian@ianrees.net</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">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&#39;s method decorators are evaluated before the view&#39;s class decorator. The class decorator looks at all the methods and sees which have been decorated with the route.<br>

<div class="im"><br>
class Routing(object):<br>
        routes = {}<br>
<br>
        @classmethod<br>
</div>        def register(cls, viewclass):<br>
                # Check a class for any methods that have routes as attributes<br>
                #       and add these to the routing dictionary<br>
                for k,v in viewclass.__dict__.items():<br>
                        if hasattr(v, &#39;route&#39;):<br>
                                print &quot;Registering %s method %s with route %s&quot;%(viewclass, v.__name__, v.route)<br>
                                cls.routes[v.route] = (viewclass, v.__name__)<br>
                return cls<br>
<div class="im"><br>
        @classmethod<br>
        def addroute(cls, route):<br>
</div>                # This decorator adds the specified routes as attributes to the methods.<br>
                # These routes will be found when the view class is registered.<br>
                def wrap(handler):<br>
                        handler.route = route<br>
                        return handler<br>
                return wrap<br>
<br>
        @classmethod<br>
        def resolve(cls, route):<br>
                for r in cls.routes:<br>
                        if r == route:<br>
                                print &quot;Found handler for %s:&quot;%route, cls.routes[r]<br>
                                view, method = cls.routes[r]<br>
                                inst = view()<br>
                                return getattr(inst, method)<br>
<br>
<br>
@Routing.register<br>
<div class="im">class View(object):<br>
        @Routing.addroute(r&#39;/blog/post/&#39;)<br>
</div>        def dosomething(self, title=None, body=None):<br>
                print &quot;Adding blog post:&quot;, title, body<br>
<br>
<br>
handler = Routing.resolve(&#39;/blog/post/&#39;)<br>
handler(title=&quot;Test&quot;, body=&quot;ok&quot;)<br>
<div class="im"><br>
<br>
<br>
Thanks,<br>
Ian<br>
<br>
On Sep 5, 2011, at 11:14 AM, Jacek Furmankiewicz wrote:<br>
<br>
</div><div><div></div><div class="h5">&gt; Hi Glyph,<br>
&gt;<br>
&gt; I looked at your suggestion, but unfortunately the implementation is very complex, if not impossible.<br>
&gt;<br>
&gt; The main problem is that<br>
&gt; a) a class method with a decorator &quot;forgets&quot; its class, so it&#39;s impossible from the decorator which class it belongs to.<br>
&gt; 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<br>
&gt;<br>
&gt; b) is is next to impossible for a class to scan its own function and find their decorators. I&#39;ve seen some hacks on StackOverflow<br>
&gt; where it actually parses the source code, but that is an ugly hack to say the least (and probably prone to many bugs)<br>
&gt;<br>
&gt; In general, it seems decorators on class methods are missing such crucial functionality as finding out which class the method belongs to.<br>
&gt; 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).<br>
&gt;<br>
&gt; if you have any suggestions on how to accomplish your recommendation, I would greatly appreciate it.<br>
&gt;<br>
&gt; The decorator in question that I would need to take out of the CorePost class and make it a standalone function looks like this:<br>
&gt;<br>
&gt;     def route(self,url,methods=(Http.GET,),accepts=MediaType.WILDCARD,produces=None,cache=True):<br>
&gt;         &quot;&quot;&quot;Main decorator for registering REST functions &quot;&quot;&quot;<br>
&gt;         def wrap(f,*args,**kwargs):<br>
&gt;             self.__registerFunction(f, url, methods, accepts, produces,cache)<br>
&gt;             return f<br>
&gt;         return wrap<br>
&gt;<br>
&gt; it&#39;s obtaining the reference to &#39;self&#39; when it is not a class method any more is the problem. Not sure how to get around it.<br>
&gt;<br>
&gt; Cheers,<br>
&gt; Jacek<br>
&gt;<br>
&gt; On Sun, Sep 4, 2011 at 12:01 AM, Glyph Lefkowitz &lt;<a href="mailto:glyph@twistedmatrix.com">glyph@twistedmatrix.com</a>&gt; wrote:<br>
&gt;<br>
&gt; On Sep 3, 2011, at 8:28 PM, Jacek Furmankiewicz wrote:<br>
&gt;<br>
&gt;&gt; Any feedback is welcome<br>
&gt;<br>
&gt; Hi Jacek,<br>
&gt;<br>
&gt; Great to see more development going into Twisted-based web stuff! :)<br>
&gt;<br>
&gt; However, I do have one question.  Maybe I&#39;m missing something about the way Flask does things, but it seems very odd to me that the decorators you&#39;re using are applied to global functions, rather than instances of an object.  For example, instead of:<br>

&gt;<br>
&gt; app = CorePost()<br>
&gt; ...<br>
&gt; @app.route(&quot;/validate/&lt;int:rootId&gt;/schema&quot;,Http.POST)<br>
&gt; @validate(schema=TestSchema)<br>
&gt; def postValidateSchema(request,rootId,childId,**kwargs):<br>
&gt;     &#39;&#39;&#39;Validate using a common schema&#39;&#39;&#39;<br>
&gt;     return &quot;%s - %s - %s&quot; % (rootId,childId,kwargs)<br>
&gt;<br>
&gt; You could do:<br>
&gt;<br>
&gt; class MyPost(CorePost):<br>
&gt;     @route(&quot;/validate/&lt;int:rootId&gt;/schema&quot;,Http.POST)<br>
&gt;     @validate(schema=TestSchema)<br>
&gt;     def postValidateSchema(self,request,rootId,childId,**kwargs):<br>
&gt;         &#39;&#39;&#39;Validate using a common schema&#39;&#39;&#39;<br>
&gt;         return &quot;%s - %s - %s&quot; % (rootId,childId,kwargs)<br>
&gt;<br>
&gt; This would allow for re-usable objects; for example, rather than having a &quot;blog article create&quot; API (sorry for the uninspired example, it&#39;s late) for your entire site, you would have a &quot;article create&quot; API on a &quot;Blog&quot;, 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.<br>

&gt;<br>
&gt; In other words, global variables are bad, and this looks like it depends rather heavily on them.<br>
&gt;<br>
&gt; Any thoughts on this?  Am I missing the point?<br>
&gt;<br>
&gt; Thanks,<br>
&gt;<br>
&gt; -glyph<br>
&gt;<br>
&gt;<br>
&gt; _______________________________________________<br>
&gt; Twisted-web mailing list<br>
&gt; <a href="mailto:Twisted-web@twistedmatrix.com">Twisted-web@twistedmatrix.com</a><br>
&gt; <a href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web" target="_blank">http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web</a><br>
&gt;<br>
&gt;<br>
&gt; _______________________________________________<br>
&gt; Twisted-web mailing list<br>
&gt; <a href="mailto:Twisted-web@twistedmatrix.com">Twisted-web@twistedmatrix.com</a><br>
&gt; <a href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web" target="_blank">http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web</a><br>
<br>
<br>
_______________________________________________<br>
Twisted-web mailing list<br>
<a href="mailto:Twisted-web@twistedmatrix.com">Twisted-web@twistedmatrix.com</a><br>
<a href="http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web" target="_blank">http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web</a><br>
</div></div></blockquote></div><br></div>