Search code examples
python-3.xcherrypy

Weird behavour of _cp_dispatch (Cherrypy)


I have an application that serves files. The methods HTTP GET/POST/PUT/DELETE should do different stuff (obviously). Therefore I wrote an object representing a file that provides the GET/POST/... methods. This file is returned by the _cp_dispatch method of a dispatcher application. See my source code.

However Cherrypy raises an exception stating that

  • TypeError: 'DispatchedFile1' object is not callable
  • TypeError: unsupported callable
  • TypeError: <__main__.DispatchedFile1 object at 0x7f209b4e7d30> is not a callable object

If I use the "normal" way of creating a web app by using an @expose d index method this sample works fine.

import cherrypy, os

@cherrypy.expose
class DispatchedFile1(object):
    def __init__(self, path):
        self._path = path

    def GET(self, start = 0, chunk_size = 0):
        if(start and chunk_size):
            return "{} ; OFFSET: {}, CHUNK_SIZE: {}".format(self._path,
                    start, chunk_size).encode("UTF-8")
        return self._path.encode("UTF-8")
class DispatchedFile2(object):
    def __init__(self, path):
        self._path = path

    @cherrypy.expose
    def index(self, start = 0, chunk_size = 0):
        if(start and chunk_size):
            return "{} ; OFFSET: {}, CHUNK_SIZE: {}".format(self._path,
                    start, chunk_size).encode("UTF-8")
        return self._path.encode("UTF-8")


class FileDispatcherApp1(object):
    def _cp_dispatch(self, vpath):
        print(vpath)

        res = DispatchedFile1("/".join(vpath))
        vpath.clear()
        print(vpath)
        return res

class FileDispatcherApp2(object):
    def _cp_dispatch(self, vpath):
        print(vpath)

        res = DispatchedFile2("/".join(vpath))
        vpath.clear()
        print(vpath)
        return res


class DummyApp(object):
    @cherrypy.expose
    def index(self):
        return "index"

cherrypy.tree.mount(FileDispatcherApp1(), "/files1")
cherrypy.tree.mount(FileDispatcherApp2(), "/files2")
cherrypy.quickstart(DummyApp(), "/")

The second version works fine but it does not fulfil my needs. Am I missing something here? How can I fix my first version?

PS.: I know that I can manually lookup the method using cherrypy.request.


Solution

  • Well, turns out I forgot to add the correct request dispatcher.

    The correct source code is:

    import cherrypy, os
    
    @cherrypy.expose
    class DispatchedFile1(object):
        def __init__(self, path):
            self._path = path
    
        def GET(self, start = 0, chunk_size = 0):
            if(start and chunk_size):
                return "{} ; OFFSET: {}, CHUNK_SIZE: {}".format(self._path,
                        start, chunk_size).encode("UTF-8")
            return self._path.encode("UTF-8")
    class DispatchedFile2(object):
        def __init__(self, path):
            self._path = path
    
        @cherrypy.expose
        def index(self, start = 0, chunk_size = 0):
            if(start and chunk_size):
                return "{} ; OFFSET: {}, CHUNK_SIZE: {}".format(self._path,
                        start, chunk_size).encode("UTF-8")
            return self._path.encode("UTF-8")
    
    
    class FileDispatcherApp1(object):
        def _cp_dispatch(self, vpath):
            print(vpath)
    
            res = DispatchedFile1("/".join(vpath))
            vpath.clear()
            print(vpath)
            return res
    
    class FileDispatcherApp2(object):
        def _cp_dispatch(self, vpath):
            print(vpath)
    
            res = DispatchedFile2("/".join(vpath))
            vpath.clear()
            print(vpath)
            return res
    
    
    class DummyApp(object):
        @cherrypy.expose
        def index(self):
            return "index"
    
    cherrypy.tree.mount(FileDispatcherApp1(), "/files1", {"/": {'request.dispatch': cherrypy.dispatch.MethodDispatcher()}})
    cherrypy.tree.mount(FileDispatcherApp2(), "/files2")
    cherrypy.quickstart(DummyApp(), "/")
    

    Note the {'request.dispatch': cherrypy.dispatch.MethodDispatcher()} when mounting the application.