Serving Static Assets

This collection of recipes describes how to serve static assets in a variety of manners.

Serving File Content Dynamically

Usually you'll use a static view (via "config.add_static_view") to serve file content that lives on the filesystem. But sometimes files need to be composed and read from a nonstatic area, or composed on the fly by view code and served out (for example, a view callable might construct and return a PDF file or an image).

By way of example, here's a Pyramid application which serves a single static file (a jpeg) when the URL /test.jpg is executed:

from pyramid.view import view_config
from pyramid.config import Configurator
from pyramid.response import FileResponse
from paste.httpserver import serve

@view_config(route_name='jpg')
def test_page(request):
    response = FileResponse(
        '/home/chrism/groundhog1.jpg',
        request=request,
        content_type='image/jpeg'
        )
    return response

if __name__ == '__main__':
    config = Configurator()
    config.add_route('jpg', '/test.jpg')
    config.scan('__main__')
    serve(config.make_wsgi_app())

Basically, use a pyramid.response.FileResponse as the response object and return it. Note that the request and content_type arguments are optional. If request is not supplied, any wsgi.file_wrapper optimization supplied by your WSGI server will not be used when serving the file. If content_type is not supplied, it will be guessed using the mimetypes module (which uses the file extension); if it cannot be guessed successfully, the application/octet-stream content type will be used.

Serving a Single File from the Root

If you need to serve a single file such as /robots.txt or /favicon.ico that must be served from the root, you cannot use a static view to do it, as static views cannot serve files from the root (a static view must have a nonempty prefix such as /static). To work around this limitation, create views "by hand" that serve up the raw file data. Below is an example of creating two views: one serves up a /favicon.ico, the other serves up /robots.txt.

At startup time, both files are read into memory from files on disk using plain Python. A Response object is created for each. This response is served by a view which hooks up the static file's URL.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# this module = myapp.views

import os

from pyramid.response import Response
from pyramid.view import view_config

# _here = /app/location/myapp

_here = os.path.dirname(__file__)

# _icon = /app/location/myapp/static/favicon.ico

_icon = open(os.path.join(
             _here, 'static', 'favicon.ico')).read()
_fi_response = Response(content_type='image/x-icon',
                        body=_icon)

# _robots = /app/location/myapp/static/robots.txt

_robots = open(os.path.join(
               _here, 'static', 'robots.txt')).read()
_robots_response = Response(content_type='text/plain',
                            body=_robots)

@view_config(name='favicon.ico')
def favicon_view(context, request):
    return _fi_response

@view_config(name='robots.txt')
def robotstxt_view(context, request):
    return _robots_response

Root-Relative Custom Static View (URL Dispatch Only)

The pyramid.static.static_view helper class generates a Pyramid view callable. This view callable can serve static assets from a directory. An instance of this class is actually used by the pyramid.config.Configurator.add_static_view() configuration method, so its behavior is almost exactly the same once it's configured.

Warning

The following example will not work for applications that use traversal, it will only work if you use URL dispatch exclusively. The root-relative route we'll be registering will always be matched before traversal takes place, subverting any views registered via add_view (at least those without a route_name). A pyramid.static.static_view cannot be made root-relative when you use traversal.

To serve files within a directory located on your filesystem at /path/to/static/dir as the result of a "catchall" route hanging from the root that exists at the end of your routing table, create an instance of the pyramid.static.static_view class inside a static.py file in your application root as below:

from pyramid.static import static_view
www = static_view('/path/to/static/dir', use_subpath=True)

Note

For better cross-system flexibility, use an asset specification as the argument to pyramid.static.static_view instead of a physical absolute filesystem path, e.g. mypackage:static instead of /path/to/mypackage/static.

Subsequently, you may wire the files that are served by this view up to be accessible as /<filename> using a configuration method in your application's startup code:

# .. every other add_route and/or add_handler declaration should come
# before this one, as it will, by default, catch all requests

config.add_route('catchall_static', '/*subpath', 'myapp.static.www')

The special name *subpath above is used by the pyramid.static.static_view view callable to signify the path of the file relative to the directory you're serving.