1

I'm trying to use CherryPy's WSGI server to serve static files, like in Using Flask with CherryPy to serve static files. Option 2 of the accepted answer there looks exactly like what I'd like to do, but I'm getting a KeyError when I try to use the static directory handler.

What I've tried:

>>>> import cherrypy
>>>> from cherrypy import wsgiserver
>>>> import os
>>>> static_handler = cherrypy.tools.staticdir.handler(section='/', dir=os.path.abspath('server_files')
>>>> d = wsgiserver.WSGIPathInfoDispatcher({'/': static_handler})
>>>> server = wsgiserver.CherryPyWSGIServer(('localhost', 12345), d)
>>>> server.start()

Then, when I try to access the server I'm getting a 500 response and the following error in the console:

KeyError('tools',)
Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/cherrypy/wsgiserver/wsgiserver2.py", line 1353, in communicate
    req.respond()
  File "/Library/Python/2.7/site-packages/cherrypy/wsgiserver/wsgiserver2.py", line 868, in respond
    self.server.gateway(self).respond()
  File "/Library/Python/2.7/site-packages/cherrypy/wsgiserver/wsgiserver2.py", line 2267, in respond
    response = self.req.server.wsgi_app(self.env, self.start_response)
  File "/Library/Python/2.7/site-packages/cherrypy/wsgiserver/wsgiserver2.py", line 2477, in __call__
    return app(environ, start_response)
  File "/Library/Python/2.7/site-packages/cherrypy/_cptools.py", line 175, in handle_func
    handled = self.callable(*args, **self._merged_args(kwargs))
  File "/Library/Python/2.7/site-packages/cherrypy/_cptools.py", line 102, in _merged_args
    tm = cherrypy.serving.request.toolmaps[self.namespace]
KeyError: 'tools'

This is displayed twice for each time I try to hit anything that the server should be able to display. When I hooked up a Flask app to the server the Flask app worked as expected, but the static file serving still gave the same error.

What do I need to do to get the staticdir.handler to work?

Community
  • 1
  • 1
Rob Watts
  • 6,866
  • 3
  • 39
  • 58
  • I having the same issue did you manage to fix it? – Thomas Turner Apr 02 '15 at 11:17
  • @ThomasTurner For the time being I'm having my Flask app serve the static files, like in [this SO answer](http://stackoverflow.com/a/20648053/2216621). The answer says that this isn't as good as serving it directly from the server (CherryPy in this case), so I'm still hoping someone will be able to answer this question. – Rob Watts Apr 02 '15 at 17:20

1 Answers1

2

I've tried various ways of getting this to work and up until today was also hitting the KeyError you have been seeing (among other issues).

I finally managed to get CherryPy to serve static alongside a Django app by adapting the code from this gist (included below).

import os
import cherrypy
from cherrypy import wsgiserver

from my_wsgi_app import wsgi

PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'public'))


class Root(object):
    pass

def make_static_config(static_dir_name):
    """
    All custom static configurations are set here, since most are common, it
    makes sense to generate them just once.
    """
    static_path = os.path.join('/', static_dir_name)
    path = os.path.join(PATH, static_dir_name)
    configuration = {static_path: {
        'tools.staticdir.on': True,
        'tools.staticdir.dir': path}
    }
    print configuration
    return cherrypy.tree.mount(Root(), '/', config=configuration)

# Assuming your app has media on diferent paths, like 'c', 'i' and 'j'
application = wsgiserver.WSGIPathInfoDispatcher({
    '/': wsgi.application,
    '/c': make_static_config('c'),
    '/j': make_static_config('j'),
    '/i': make_static_config('i')})

server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 8070), application,
                                       server_name='www.cherrypy.example')
try:
    server.start()
except KeyboardInterrupt:
    print "Terminating server..."
    server.stop()

Hopefully wrapping a Flask app will be fairly similar.

The key for me was using the cherrypy.tree.mount on a dummy class, rather than trying to use the staticdir.handler directly.

For the curious - I used the code in the gist to customise a version of django-cherrypy's runcpserver management command, although in hindsight it would probably have been easier to create a new command from scratch.

Good luck (and thanks to Alfredo Deza)!

robashdown
  • 63
  • 1
  • 5