2

Embarrassed to ask but I am using webapp2 and I am templating out a solution to make it easier to define routesbased on this google webapp2 route function. But it all depends on being able to define TYPE_NAME at the child level. The idea is the parent sets everything up and the child just needs to implement the _list function. The issue I ran into is TYPE_NAME is None and I need it to be the child.

#main WSGI is extended to have this function 
class WSGIApplication(webapp2.WSGIApplication):
    def route(self, *args, **kwargs):
        def wrapper(func):
            self.router.add(webapp2.Route(handler=func, *args, **kwargs))
            return func

        return wrapper

from main import application
class ParentHandler(RequestHandler):
    TYPE_NAME = None

    @application.route('/', name="list_%s" %TYPE_NAME)
    def list(self):
          return self._list()

class ChildHandler(ParentHandler):
    TYPE_NAME = 'child'

    def _list(self):
         return []

I have tried a couple solutions using "class properties" but they didn't pan out. Open to other ideas, I basically just need the child class to inherit the decorated properties and execute them.

Edit:

For all of those on the edge of their seats wondering how I fix this,I was not able to get everything I needed out of the decorator so I ended up using a meta. I also added a _URLS parameter to allow for adding additional "routes". It maps custom function to the route. Really wanted to use a decorator but couldn't get it to work.

class RequestURLMeta(type):
    def __new__(mcs, name, bases, dct):
        result = super(RequestURLMeta, mcs).__new__(mcs, name, bases, dct)
        urls = getattr(result, '_URLS', {}) or {}
        for k,v in urls.iteritems():
            template = v.pop('template')
            app.route(getattr(result, k), template, **v)

        if getattr(result, 'TYPE_NAME', None):
            app.route(result.list, result.ROOT_PATH, methods=['GET'],name="%s" % result.TYPE_NAME)
        #other ones went here..

    return result

class ParentHandler(RequestHandler):
    __metaclass__ = RequestURLMeta


class ChildHandler(ParentHandler):
    TYPE_NAME = 'child'
    _URLS = { 'custom': '/custom', 'TYPE_NAME': 'custom_test' }
    def _list(self):
        return []
    def custom(self):  pass
Nix
  • 57,072
  • 29
  • 149
  • 198
  • What is the `application.route()` call doing? Is this where you want the `TYPE_NAME` defined in the child to be used? – Andrew Clark Jan 25 '13 at 18:00
  • @F.J I upated the post added the route function and updated the decorator to be more descriptive. – Nix Jan 25 '13 at 18:02
  • Oh so is that a decorator for the `list()` function? If so it should be `@application.route(...)`. – Andrew Clark Jan 25 '13 at 18:05

2 Answers2

1

I think to get this to work you are going to need to use a metaclass. It might look something like the following (untested):

from main import application

class RouteMeta(type):
    def __new__(mcs, name, bases, dct):
        type_name = dct.get("TYPE_NAME")
        if type_name is not None:
            @application.route('/', type_name)
            def list(self):
                return self._list()
            dct["list"] = list
        return super(RouteMeta, mcs).__new__(mcs, name, bases, dct)

class ParentHandler(RequestHandler):
    __metaclass__ = RouteMeta

class ChildHandler(ParentHandler):
    TYPE_NAME = 'child'

    def _list(self):
         return []

Instead of having the list() method an attribute of ParentHandler, it is dynamically created for classes that inherit from ParentHandler and have TYPE_NAME defined.

If RequestHandler also uses a custom metaclass, have RouteMeta inherit from RequestHandler.__metaclass__ instead of type.

Community
  • 1
  • 1
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • I was able to take this and work with it... the big thing was instead of doing the `type_name = ` code before the `super` call I took the result of the `super` call and just patched it there. I posted a dumbed down example above. Thanks again. – Nix Jan 28 '13 at 16:45
0

This code:

@application.route('/', name="list_%s" %TYPE_NAME)
def list(self):*emphasized text*
    ...

is semantically identical to this one:

def list(self):
    ...
list = application.route('/', name="list_%s" %TYPE_NAME)(list)

i.e. the method route is called inside the ParentHandler scope and whatever lazy method you try, it will not work. You should try something different:

from main import application

def route_list(klass):
    klass.list = application.route('/',
        name="list_%s" % klass.TYPE_NAME)(klass.list)
    return klass

class ParentHandler(RequestHandler):

    def list(self):
          return self._list()

class ChildHandler(ParentHandler):
    TYPE_NAME = 'child'

    def _list(self):
         return []

# in python3 would be:
# @route_list
# class ChildHandler(ParentHandler):
#   ...
ChildHandler = route_list(ChildHandler)
mg.
  • 7,822
  • 1
  • 26
  • 30
  • This is actually what I was leaning to... a class decorator to set all of the routes up. do they only work python3x ? – Nix Jan 25 '13 at 18:25