Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Flask provide CherryPy style routing?

The default CherryPy routing style is based on instances of classes with methods decorated by @cherrypy.expose.

In the example below, these urls are provided by simple tweaking on otherwise ordinary classes.

/
/hello
/hello/again
/bye
/bye/again

I wonder if there is a way to achieve this using Flask's @route or some other decorator.

import cherrypy

class Root(object):
    @cherrypy.expose
    def index(self):
        return 'my app'

class Greeting(object):
    def __init__(self, name, greeting):
        self.name = name
        self.greeting = greeting

    @cherrypy.expose
    def index(self):
        return '%s %s!' %(self.greeting, self.name)

    @cherrypy.expose
    def again(self):
        return '%s again, %s!' %(self.greeting, self.name)

if __name__ == '__main__':
    root = Root()
    root.hello = Greeting('Foo', 'Hello')
    root.bye = Greeting('Bar', 'Bye')
    cherrypy.quickstart(root)
like image 640
Xuan Avatar asked Aug 16 '12 16:08

Xuan


People also ask

How do you routing in Flask?

Routing in FlaskThe route() decorator in Flask is used to bind an URL to a function. As a result when the URL is mentioned in the browser, the function is executed to give the result. Here, URL '/hello' rule is bound to the hello_world() function.

What is the app route decorator used for in a Flask application?

We then use the route() decorator to tell Flask what URL should trigger our function. The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user's browser.

How does a Flask route work?

App Routing means mapping the URLs to a specific function that will handle the logic for that URL. Modern web frameworks use more meaningful URLs to help users remember the URLs and make navigation simpler. Example: In our application, the URL (“/”) is associated with the root URL.

How do I start a python Flask?

Setting up Flask Flask 2.0 is easy to set up. Use pip install flask to install both Flask and all of its dependencies including the Jinja2 templating system. As with any Python framework, it's best to create a project using Flask inside a Python virtual environment.


1 Answers

In order to do something like this you'll have to use a bit of python magic, however, it's absolutely possible to do with flask. The most straight-forward way to copy your example would be to subclass flask. This is one way to do it:

import inspect
from flask import Flask


def expose(f):
    """Decorator that flags a method to be exposed"""
    f._exposed_method = True
    return f


class FlaskOfCherryPy(Flask):
    """Custom flask that allows cherrypy's expose decorator"""
    def quickstart(self, root_handler, *args, **kwargs):
        self._process_root_handler(root_handler)
        self.run(*args, **kwargs)

    def _process_root_handler(self, root_handler):
        # Prime the recursive processing
        root_url = []
        self._process_a_handler(root_handler, root_url)

    def _process_a_handler(self, current_handler, url_stack):
        # This gives a list of all the members of current_handler
        members = inspect.getmembers(current_handler)
        for name, value in members:
            # You probably want to skip things that start with a _ or __
            if name.startswith('_'):
                continue

            # Check if the method is decorated
            is_exposed_method = getattr(value, '_exposed_method', False)

            # If it's a callable with the _exposed_method attribute set
            # Then it's an exposed method
            if is_exposed_method and callable(value):
                self._add_exposed_url(url_stack, name, value)
            else:
                new_stack = url_stack[:]
                new_stack.append(name)
                self._process_a_handler(value, new_stack)

    def _add_exposed_url(self, url_stack, name, view_func):
        copied_stack = url_stack[:]

        if name != 'index':
            copied_stack.append(name)

        url = "/%s" % "/".join(copied_stack)

        if name == 'index':
            copied_stack.append(name)

        view_name = "_".join(copied_stack)
        self.add_url_rule(url, view_name, view_func)


class Root(object):
    @expose
    def index(self):
        return 'my app'


class Greeting(object):
    def __init__(self, name, greeting):
        self.name = name
        self.greeting = greeting

    @expose
    def index(self):
        return '%s %s!' %(self.greeting, self.name)

    @expose
    def again(self):
        return '%s again, %s!' %(self.greeting, self.name)


if __name__ == '__main__':
    root = Root()
    root.hello = Greeting('Foo', 'Hello')
    root.bye = Greeting('Bar', 'Bye')
    app = FlaskOfCherryPy(__name__)
    app.quickstart(root)

Essentially the trick is grabbing all methods that are tagged with the _exposed_method attribute and passing them to the Flask.add_url_rule (see docs here). The beauty of flask is that it's such a lightweight system that it isn't very scary to extend it. I highly suggest diving in yourself and giving it a shot, but I had fun solving your question so I have the script as a gist here.

This particular code I've written isn't perfect and hasn't been heavily tested, but it definitely works for your particular use case. Also, it's not necessarily how you would want to run the app in production. You'd have to create some kind of application factory to do it. Again, I highly suggest looking into the internals of flask to make it do what you want. You may also want to look at class-based views or blueprints that flask offers. They're able to some things similarly to how you had them here. Granted, using them is very different and setting instance attributes is not something I know to be possible using vanilla blueprints. Again, you can always extend :-)

like image 173
ravenac95 Avatar answered Sep 19 '22 16:09

ravenac95