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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
.. index::
single: assets
.. _assets_chapter:
Assets
======
An :term:`asset` is any file contained within a Python :term:`package` which
is *not* a Python source code file. For example, each of the following is an
asset:
- a :term:`Chameleon` template file contained within a Python package.
- a GIF image file contained within a Python package.
- a CSS file contained within a Python package.
- a JavaScript source file contained within 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).
The use of assets is quite common in most web development projects. For
example, when you create a :app:`Pyramid` application using one of the
available "paster" templates, as described in :ref:`creating_a_project`, the
directory representing the application contains a Python :term:`package`.
Within that Python package, there are directories full of files which are
assets. For example, there is a ``templates`` directory which contains
``.pt`` files, and a ``static`` directory which contains ``.css``, ``.js``,
and ``.gif`` files.
.. _understanding_assets:
Understanding Assets
--------------------
Let's imagine you've created a :app:`Pyramid` application that uses a
:term:`Chameleon` ZPT template via the
:func:`pyramid.chameleon_zpt.render_template_to_response` API. For example,
the application might address the asset named ``templates/some_template.pt``
using that API within a ``views.py`` file inside a ``myapp`` package:
.. ignore-next-block
.. code-block:: python
:linenos:
from pyramid.chameleon_zpt import render_template_to_response
render_template_to_response('templates/some_template.pt')
"Under the hood", when this API is called, :app:`Pyramid` attempts
to make sense out of the string ``templates/some_template.pt``
provided by the developer. To do so, it first finds the "current"
package. The "current" package is the Python package in which the
``views.py`` module which contains this code lives. This would be the
``myapp`` package, according to our example so far. By resolving the
current package, :app:`Pyramid` has enough information to locate
the actual template file. These are the elements it needs:
- The *package name* (``myapp``)
- The *asset name* (``templates/some_template.pt``)
:app:`Pyramid` uses the :term:`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.
Package names often contain dots. For example, ``pyramid`` is a package.
Asset names usually look a lot like relative UNIX file paths.
.. index::
pair: overriding; assets
.. _overriding_assets_section:
Overriding Assets
-----------------
It can often be useful to override specific assets from "outside" a given
:app:`Pyramid` application. For example, you may wish to reuse an existing
:app:`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, :app:`Pyramid` contains a feature that makes it possible to
"override" one asset with one or more other assets. In support of this
feature, a :term:`Configurator` API exists named
:meth:`pyramid.config.Configurator.override_asset`. This API allows you to
*override* the following kinds of assets defined in any Python package:
- Individual :term:`Chameleon` templates.
- A directory containing multiple Chameleon templates.
- Individual static files served up by an instance of the
``pyramid.view.static`` helper class.
- A directory of static files served up by an instance of the
``pyramid.view.static`` helper class.
- Any other asset (or set of assets) addressed by code that uses the
setuptools :term:`pkg_resources` API.
.. note:: The :term:`ZCML` directive named ``asset`` serves the same purpose
as the :meth:`pyramid.config.Configurator.override_asset` method.
.. index::
single: override_asset
.. _override_asset:
The ``override_asset`` API
~~~~~~~~~~~~~~~~~~~~~~~~~~
An individual call to :meth:`pyramid.config.Configurator.override_asset`
can override a single asset. For example:
.. ignore-next-block
.. code-block:: python
:linenos:
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 :term:`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:
.. ignore-next-block
.. code-block:: python
:linenos:
config.override_asset(to_override='some.package',
override_with='another.package')
Individual subdirectories within a package can also be overridden:
.. ignore-next-block
.. code-block:: python
:linenos:
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
:class:`pyramid.config.Configurator` class construction).
For example:
.. ignore-next-block
.. code-block:: python
:linenos:
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
:func:`pkg_resources.get_resource_filename`,
:func:`pkg_resources.get_resource_stream` or
:func:`pkg_resources.get_resource_string` APIs will obtain an overridden file
when an override is used.
|