shoopdelang
shoopdelang

Reputation: 995

Routing to method based on string

I'm trying to call a method based on a string in Python similar to the way Tornado routes requests based on the URL. So for example, if I had the string "/hello" I want to call the method "hello" in some class. I've been looking at Python Routes and came up the following solution:

from routes import Mapper

class Test():
    def hello(self):
        print "hello world"

map = Mapper()
map.connect(None, '/hello', controller=Test(), action='hello')

match = map.match("/hello")
if match != None:
    getattr(match['controller'], match['action'])()

However I would like to add the ability to pass parts of the string as arguments to the method. So for example, with the string "hello/5" I want to call hello(5) (again very similar to Tornado's routing). This should work for an arbitrary number of arguments. Does anyone have a good approach to this? Would it be possible to use Tornado's routing capabilities on a string that is not the request URL?

Upvotes: 3

Views: 1527

Answers (2)

rowland.lancer
rowland.lancer

Reputation: 21

Similar thoughts,different way. Here is the code:

class Xroute(tornado.web.RequestHandler):
    def get(self,path):
        Router().get(path,self)
    def post(self,path):
        Router().post(path,self)

class Router(object):
    mapper={}
    @classmethod
    def route(cls,**deco):
        def foo(func):
            url = deco.get('url') or '/'
            if url not in cls.mapper:
                method = deco.get('method') or 'GET'
                mapper_node = {}
                mapper_node['method'] = method
                mapper_node['call'] = func
                cls.mapper[url] = mapper_node
            return func
        return foo

    def get(self,path,reqhandler):
        self.emit(path,reqhandler,"GET")

    def post(self,path,reqhandler):
        self.emit(path,reqhandler,"POST")

    def emit(self,path,reqhandler,method_flag):
        mapper = Router.mapper
        for urlExp in mapper:
            m = re.match('^'+urlExp+'$',path)
            if m:
                params = (reqhandler,)
                for items in m.groups():
                    params+=(items,)
                mapper_node = mapper.get(urlExp)
                method = mapper_node.get('method')
                if method_flag not in method:
                    raise tornado.web.HTTPError(405)
                call = mapper_node.get('call')
                try:
                    call(*params)
                    break
                except Exception,e:
                    print e
                    raise tornado.web.HTTPError(500)
        else:
            raise tornado.web.HTTPError(404)

@Router.route(url=r"hello/([a-z]+)",method="GET")
def test(req, who):
    #http://localhost:8888/hello/billy
    req.write("Hi," + who)

@Router.route(url=r"greetings/([a-z]+)",method="GET|POST")
def test2(req, who):
    #http://localhost:8888/greetings/rowland
    test(req, who)

@Router.route(url=r"book/([a-z]+)/(\d+)",method="GET|POST")
def test3(req,categories,bookid):
    #http://localhost:8888/book/medicine/49875
    req.write("You are looking for a " + categories + " book\n"+"book No. " + bookid)

@Router.route(url=r"person/(\d+)",method="GET|POST")
def test4(req, personid):
    #http://localhost:8888/person/29772
    who = req.get_argument("name","default")
    age = req.get_argument("age",0)
    person = {}
    person['id'] = personid
    person['who'] = who
    person['age'] = int(age)
    req.write(person)

if __name__=="__main__":
    port = 8888
    application = tornado.web.Application([(r"^/([^\.|]*)(?!\.\w+)$", Xroute),])
    application.listen(port)
    tornado.ioloop.IOLoop.instance().start()

Upvotes: 1

alecxe
alecxe

Reputation: 473863

First of all, would be good to know what is the motivation, the use case, the initial task for making such a "local" routing system.

Relying on your example, one way to go is to actually build something on your own.

Here's a very basic example:

class Test():
    def hello(self, *args):
        print args


urls = ['hello/', 'hello/5', 'hello/5/world/so/good']
handler = Test()

for url in urls:
    parts = url.split('/')
    method = parts[0]
    if hasattr(handler, method):
        getattr(handler, method)(*parts[1:])

prints:

('',)
('5',)
('5', 'world', 'so', 'good')

You can also make use of regex to save parts of the url as named or positional groups. As an example, see how django, python-routes are doing it.

Hope that helps.

Upvotes: 1

Related Questions