Mangle function name with decorator?

R. David Murray rdmurray at bitdance.com
Wed Mar 18 13:22:12 EDT 2009


Adam <adam.crossland at gmail.com> wrote:
> David, would you believe that I just posted about this very idea,  It
> doesn't seem to have shown up yet, though.  This idea works from the
> perspective of being trivially easy to implement.  I can easily write
> a metaclass that looks in the namespace for methods with names that
> start with GET_or POST_, or I can override __getattribute__ to do the
> look up that way.  However, there are a couple of weaknesses that I
> see with this approach.
> 
> First, from a purely aesthetic point of view, prepending the desired
> verb to the method name just looks a bit ugly.  Also, it makes it
> difficult to deal elegantly with avoiding duplicating code when one
> piece of logic should dealing with more than one verb.  So, if I want
> to have one method that works for GET and POST, I can do this:
> 
> def GET_foo(self):
>     # Do stuff for GET
> 
> def POST_foo(self):
>     return self.GET_foo()
> 
> but then I feel like I am cluttering my controller code with unneeded
> functions when writing
> @GET
> @POST
> def foo(self):
>    # Blah blah blah
> 
> would be so much neater.

    def GET_foo(self):
        #do stuff for GET/POST

    POST_foo = GET_foo

> Or, I could allow method signatures like:
> def GET_POST_foo(self):
>    # Blah, blah, blah
> 
> But now my code to parse and manipulate or do lookups on methods names
> is much more complicated.  Also, it introduces difficult ambiguities
> in the case that an action of the controller has the same name as an
> HTTP Verb.  These ambiguities can be coded around, but doing so makes

Hmm.  I don't follow that.  GET_GET would seem to be unambiguous.

> the code more-and-more crufty and prone to breakage.  I don't want to
> build too much of a Rube Goldberg machine here, right?  What I really
> want to do is use Python's metaprogamming facilities to provide an
> elegant solution to a problem.  Unfortunately, I really don't think
> that it is going to work out in any way that is really satisfying.

Someone else suggested the property model, though that is probably not
as elegant as you want either.

So....how about the below.  Extending it to handle multiple classes is
left as an exercise for the reader :)  As is extending it to handle
stacking the decorators.

--
R. David Murray           http://www.bitdance.com
------------------------------------------------------------------------

registry = {}

def HTTP_decorator_factory(name):
    def _(f):
        registry['%s_%s' % (name, f.__name__)] = f
        def _(self, *args, **kw):
            return _dispatch(f.__name__, self, *args, **kw)
        return _
    return _

def _dispatch(name, self, *args, **kw):
    return registry['%s_%s' % (self.mode, name)](self, *args, **kw)

GET = HTTP_decorator_factory('GET')
POST = HTTP_decorator_factory('POST')


class Controller(object):

    def __init__(self, mode='GET'):
        self.mode = mode

    @POST
    def foo(self):
        print "in POST foo"

    @GET
    def foo(self):
        print "in GET foo"


c1 = Controller(mode='GET')
c2 = Controller(mode='POST')

c1.foo()
c2.foo()




More information about the Python-list mailing list