single: assets single: static asssets
An asset
is any file contained within a Python package
which is not a Python source code file. For example, each of the following is an asset:
- a GIF image file contained within a Python package or contained within any subdirectory of a Python package.
- a CSS file contained within a Python package or contained within any subdirectory of a Python package.
- a JavaScript source file contained within a Python package or contained within any subdirectory of a Python package.
- A directory within a package that does not have an
__init__.py
in it (if it possessed an__init__.py
it would be a package). - a
Chameleon
orMako
template file contained within a Python package.
The use of assets is quite common in most web development projects. For example, when you create a Pyramid
application using one of the available scaffolds, as described in creating_a_project
, the directory representing the application contains a Python package
. Within that Python package, there are directories full of files which are static assets. For example, there's a static
directory which contains .css
, .js
, and .gif
files. These asset files are delivered when a user visits an application URL.
single: asset specifications
Let's imagine you've created a Pyramid
application that uses a Chameleon
ZPT template via the pyramid.renderers.render_to_response
API. For example, the application might address the asset using the asset specification
myapp:templates/some_template.pt
using that API within a views.py
file inside a myapp
package:
from pyramid.renderers import render_to_response
render_to_response('myapp:templates/some_template.pt', {}, request)
"Under the hood", when this API is called, Pyramid
attempts to make sense out of the string myapp:templates/some_template.pt
provided by the developer. This string is an asset specification
. It is composed of two parts:
- The package name (
myapp
) - The asset name (
templates/some_template.pt
), relative to the package directory.
The two parts are separated by the colon character.
Pyramid
uses the Python pkg_resources
API to resolve the package name and asset name to an absolute (operating-system-specific) file name. It eventually passes this resolved absolute filesystem path to the Chameleon templating engine, which then uses it to load, parse, and execute the template file.
There is a second form of asset specification: a relative asset specification. Instead of using an "absolute" asset specification which includes the package name, in certain circumstances you can omit the package name from the specification. For example, you might be able to use templates/mytemplate.pt
instead of myapp:templates/some_template.pt
. Such asset specifications are usually relative to a "current package." The "current package" is usually the package which contains the code that uses the asset specification. Pyramid
APIs which accept relative asset specifications typically describe what the asset is relative to in their individual documentation.
single: add_static_view pair: assets; serving
Pyramid
makes it possible to serve up static asset files from a directory on a filesystem to an application user's browser. Use the pyramid.config.Configurator.add_static_view
to instruct Pyramid
to serve static assets such as JavaScript and CSS files. This mechanism makes a directory of static files available at a name relative to the application root URL, e.g. /static
or as an external URL.
Note
~pyramid.config.Configurator.add_static_view
cannot serve a single file, nor can it serve a directory of static files directly relative to the root URL of a Pyramid
application. For these features, see advanced_static
.
Here's an example of a use of ~pyramid.config.Configurator.add_static_view
that will serve files up from the /var/www/static
directory of the computer which runs the Pyramid
application as URLs beneath the /static
URL prefix.
# config is an instance of pyramid.config.Configurator
config.add_static_view(name='static', path='/var/www/static')
The name
prepresents a URL prefix. In order for files that live in the path
directory to be served, a URL that requests one of them must begin with that prefix. In the example above, name
is static
, and path
is /var/www/static
. In English, this means that you wish to serve the files that live in /var/www/static
as sub-URLs of the /static
URL prefix. Therefore, the file /var/www/static/foo.css
will be returned when the user visits your application's URL /static/foo.css
.
A static directory named at path
may contain subdirectories recursively, and any subdirectories may hold files; these will be resolved by the static view as you would expect. The Content-Type
header returned by the static view for each particular type of file is dependent upon its file extension.
By default, all files made available via ~pyramid.config.Configurator.add_static_view
are accessible by completely anonymous users. Simple authorization can be required, however. To protect a set of static files using a permission, in addition to passing the required name
and path
arguments, also pass the permission
keyword argument to ~pyramid.config.Configurator.add_static_view
. The value of the permission
argument represents the permission
that the user must have relative to the current context
when the static view is invoked. A user will be required to possess this permission to view any of the files represented by path
of the static view. If your static assets must be protected by a more complex authorization scheme, see advanced_static
.
Here's another example that uses an asset specification
instead of an absolute path as the path
argument. To convince ~pyramid.config.Configurator.add_static_view
to serve files up under the /static
URL from the a/b/c/static
directory of the Python package named some_package
, we can use a fully qualified asset
specification
as the path
:
# config is an instance of pyramid.config.Configurator
config.add_static_view(name='static', path='some_package:a/b/c/static')
The path
provided to ~pyramid.config.Configurator.add_static_view
may be a fully qualified asset specification
or an absolute path.
Instead of representing a URL prefix, the name
argument of a call to ~pyramid.config.Configurator.add_static_view
can alternately be a URL. Each of examples we've seen so far have shown usage of the name
argument as a URL prefix. However, when name
is a URL, static assets can be served from an external webserver. In this mode, the name
is used as the URL prefix when generating a URL using pyramid.request.Request.static_url
.
For example, ~pyramid.config.Configurator.add_static_view
may be fed a name
argument which is http://example.com/images
:
# config is an instance of pyramid.config.Configurator
config.add_static_view(name='http://example.com/images',
path='mypackage:images')
Because ~pyramid.config.Configurator.add_static_view
is provided with a name
argument that is the URL http://example.com/images
, subsequent calls to ~pyramid.request.Request.static_url
with paths that start with the path
argument passed to ~pyramid.config.Configurator.add_static_view
will generate a URL something like http://example.com/images/logo.png
. The external webserver listening on example.com
must be itself configured to respond properly to such a request. The ~pyramid.request.Request.static_url
API is discussed in more detail later in this chapter.
single: generating static asset urls single: static asset urls pair: assets; generating urls
When a ~pyramid.config.Configurator.add_static_view
method is used to register a static asset directory, a special helper API named pyramid.request.Request.static_url
can be used to generate the appropriate URL for an asset that lives in one of the directories named by the static registration path
attribute.
For example, let's assume you create a set of static declarations like so:
config.add_static_view(name='static1', path='mypackage:assets/1')
config.add_static_view(name='static2', path='mypackage:assets/2')
These declarations create URL-accessible directories which have URLs that begin with /static1
and /static2
, respectively. The assets in the assets/1
directory of the mypackage
package are consulted when a user visits a URL which begins with /static1
, and the assets in the assets/2
directory of the mypackage
package are consulted when a user visits a URL which begins with /static2
.
You needn't generate the URLs to static assets "by hand" in such a configuration. Instead, use the ~pyramid.request.Request.static_url
API to generate them for you. For example:
from pyramid.chameleon_zpt import render_template_to_response
def my_view(request):
css_url = request.static_url('mypackage:assets/1/foo.css')
js_url = request.static_url('mypackage:assets/2/foo.js')
return render_template_to_response('templates/my_template.pt',
css_url = css_url,
js_url = js_url)
If the request "application URL" of the running system is http://example.com
, the css_url
generated above would be: http://example.com/static1/foo.css
. The js_url
generated above would be http://example.com/static2/foo.js
.
One benefit of using the ~pyramid.request.Request.static_url
function rather than constructing static URLs "by hand" is that if you need to change the name
of a static URL declaration, the generated URLs will continue to resolve properly after the rename.
URLs may also be generated by ~pyramid.request.Request.static_url
to static assets that live outside the Pyramid
application. This will happen when the ~pyramid.config.Configurator.add_static_view
API associated with the path fed to ~pyramid.request.Request.static_url
is a URL instead of a view name. For example, the name
argument may be http://example.com
while the the path
given may be mypackage:images
:
config.add_static_view(name='http://example.com/images',
path='mypackage:images')
Under such a configuration, the URL generated by static_url
for assets which begin with mypackage:images
will be prefixed with http://example.com/images
:
request.static_url('mypackage:images/logo.png')
# -> http://example.com/images/logo.png
Using ~pyramid.request.Request.static_url
in conjunction with a ~pyramid.configuration.Configurator.add_static_view
makes it possible to put static media on a separate webserver during production (if the name
argument to ~pyramid.config.Configurator.add_static_view
is a URL), while keeping static media package-internal and served by the development webserver during development (if the name
argument to ~pyramid.config.Configurator.add_static_view
is a URL prefix). To create such a circumstance, we suggest using the pyramid.registry.Registry.settings
API in conjunction with a setting in the application .ini
file named media_location
. Then set the value of media_location
to either a prefix or a URL depending on whether the application is being run in development or in production (use a different .ini
file for production than you do for development). This is just a suggestion for a pattern; any setting name other than media_location
could be used.
single: static assets view
For more flexibility, static assets can be served by a view callable
which you register manually. For example, if you're using URL
dispatch
, you may want static assets to only be available as a fallback if no previous route matches. Alternately, you might like to serve a particular static asset manually, because its download requires authentication.
Note that you cannot use the ~pyramid.request.Request.static_url
API to generate URLs against assets made accessible by registering a custom static view.
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
static view cannot be made root-relative when you use traversal unless it's registered as a Not Found view
.
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
static_view = 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 declaration should come
# before this one, as it will, by default, catch all requests
config.add_route('catchall_static', '/*subpath')
config.add_view('myapp.static.static_view', route_name='catchall_static')
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.
You can register a simple view callable to serve a single static asset. To do so, do things "by hand". First define the view callable.
import os
from pyramid.response import Response
def favicon_view(request):
here = os.path.dirname(__file__)
icon = open(os.path.join(here, 'static', 'favicon.ico'))
return Response(content_type='image/x-icon', app_iter=icon)
The above bit of code within favicon_view
computes "here", which is a path relative to the Python file in which the function is defined. It then uses the Python open
function to obtain a file handle to a file within "here" named static
, and returns a response using the open the file handle as the response's app_iter
. It makes sure to set the right content_type too.
You might register such a view via configuration as a view callable that should be called as the result of a traversal:
config.add_view('myapp.views.favicon_view', name='favicon.ico')
Or you might register it to be the view callable for a particular route:
config.add_route('favicon', '/favicon.ico')
config.add_view('myapp.views.favicon_view', route_name='favicon')
Because this is a simple view callable, it can be protected with a permission
or can be configured to respond under different circumstances using view predicate
arguments.
pair: overriding; assets
It can often be useful to override specific assets from "outside" a given Pyramid
application. For example, you may wish to reuse an existing Pyramid
application more or less unchanged. However, some specific template file owned by the application might have inappropriate HTML, or some static asset (such as a logo file or some CSS file) might not be appropriate. You could just fork the application entirely, but it's often more convenient to just override the assets that are inappropriate and reuse the application "as is". This is particularly true when you reuse some "core" application over and over again for some set of customers (such as a CMS application, or some bug tracking application), and you want to make arbitrary visual modifications to a particular application deployment without forking the underlying code.
To this end, Pyramid
contains a feature that makes it possible to "override" one asset with one or more other assets. In support of this feature, a Configurator
API exists named pyramid.config.Configurator.override_asset
. This API allows you to override the following kinds of assets defined in any Python package:
- Individual
Chameleon
templates. - A directory containing multiple Chameleon templates.
- Individual static files served up by an instance of the
pyramid.static.static_view
helper class. - A directory of static files served up by an instance of the
pyramid.static.static_view
helper class. - Any other asset (or set of assets) addressed by code that uses the setuptools
pkg_resources
API.
single: override_asset
An individual call to ~pyramid.config.Configurator.override_asset
can override a single asset. For example:
config.override_asset(
to_override='some.package:templates/mytemplate.pt',
override_with='another.package:othertemplates/anothertemplate.pt')
The string value passed to both to_override
and override_with
sent to the override_asset
API is called an asset specification
. The colon separator in a specification separates the package name from the asset name. The colon and the following asset name are optional. If they are not specified, the override attempts to resolve every lookup into a package from the directory of another package. For example:
config.override_asset(to_override='some.package',
override_with='another.package')
Individual subdirectories within a package can also be overridden:
config.override_asset(to_override='some.package:templates/',
override_with='another.package:othertemplates/')
If you wish to override a directory with another directory, you must make sure to attach the slash to the end of both the to_override
specification and the override_with
specification. If you fail to attach a slash to the end of a specification that points to a directory, you will get unexpected results.
You cannot override a directory specification with a file specification, and vice versa: a startup error will occur if you try. You cannot override an asset with itself: a startup error will occur if you try.
Only individual package assets may be overridden. Overrides will not traverse through subpackages within an overridden package. This means that if you want to override assets for both some.package:templates
, and some.package.views:templates
, you will need to register two overrides.
The package name in a specification may start with a dot, meaning that the package is relative to the package in which the configuration construction file resides (or the package
argument to the ~pyramid.config.Configurator
class construction). For example:
config.override_asset(to_override='.subpackage:templates/',
override_with='another.package:templates/')
Multiple calls to override_asset
which name a shared to_override
but a different override_with
specification can be "stacked" to form a search path. The first asset that exists in the search path will be used; if no asset exists in the override path, the original asset is used.
Asset overrides can actually override assets other than templates and static files. Any software which uses the pkg_resources.get_resource_filename
, pkg_resources.get_resource_stream
or pkg_resources.get_resource_string
APIs will obtain an overridden file when an override is used.