Reputation: 995
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
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
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