falcon icon indicating copy to clipboard operation
falcon copied to clipboard

Function Resources

Open nZac opened this issue 5 years ago • 4 comments

Right now Falcon requires you to use classes to define resource handlers, like this:

class MyResource:
    def on_get(self, req, resp):
        resp.body = 'hello!'
api.add_route('/', MyResource())

The community (#1455, #1499) has asked to remove the class requirement and enable direct function support.

def get_resource(req, resp):
    resp.body = 'hello!'

api.add_get_route('/', get_resource)

nZac avatar May 07 '19 14:05 nZac

While it is always tempting to add new cool features, it would be interesting to hear which use cases we address by implementing it (I'm not implying it is necessarily a bad idea).

Alternatively we could start off by creating a recipe (or a Falcon add-on) illustrating how one can shim falcon.API (renamed to falcon.App in Falcon 3.0+) in case this functionality is important to have. I have actually created a proof-of-concept ~Gist -- removed as I was cleaning my Gists a bit~ illustrating that:

# Demonstrating how "function resources" (see https://github.com/falconry/falcon/issues/1532)
# can be shimmed in case anyone *really* needs that feature

import functools

import falcon


class FunctionResourcesAPI(falcon.API):

    def _add_method_route(self, uri_template, responder=None, **kwargs):
        class Shim:
            def on_request(self, req, resp, **kwargs):
                responder(req, resp, **kwargs)

        kwargs.pop('suffix', None)
        http_verb = kwargs.pop('http_verb')

        shim = Shim()
        setattr(shim, 'on_' + http_verb.lower(), shim.on_request)
        self.add_route(uri_template, shim, **kwargs)


for _http_verb in falcon.COMBINED_METHODS:
    setattr(
        FunctionResourcesAPI,
        'add_' + _http_verb.lower() + '_route',
        functools.partialmethod(FunctionResourcesAPI._add_method_route,
                                http_verb=_http_verb))


def say_hello(req, resp, name):
    resp.media = {'message': 'Hello, {}!'.format(name)}


api = FunctionResourcesAPI()
api.add_get_route('/hello/{name}', say_hello)
GET /hello/Developer HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8000
User-Agent: HTTPie/0.9.9



HTTP/1.1 200 OK
Connection: close
Date: Mon, 28 Oct 2019 17:36:57 GMT
Server: gunicorn/19.9.0
content-length: 32
content-type: application/json

{
    "message": "Hello, Developer!"
}

vytas7 avatar Oct 28 '19 17:10 vytas7

Assigning this to 3.1 just so we don't forget to make a decision on it.

kgriffs avatar Oct 28 '19 17:10 kgriffs

Adding to what @vytas7 said, we should also consider what patterns this will encourage/discourage and whether those are good things in the long run for the community.

kgriffs avatar Oct 28 '19 17:10 kgriffs

Another reaction off the top of my head: when deciding on this, we might want to compare and contrast the deliverables to what already is available in the Falcon ecosystem. For instance, if the goal is to easily expose a bunch of arbitrary functions on HTTP, one can just plop Hug (https://github.com/hugapi/hug) decorators on them, and so on.

vytas7 avatar Oct 28 '19 20:10 vytas7