summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/static.rst2
-rw-r--r--docs/narr/assets.rst41
-rw-r--r--pyramid/config/views.py8
-rw-r--r--pyramid/static.py66
-rw-r--r--pyramid/tests/test_config/test_views.py20
-rw-r--r--pyramid/tests/test_static.py46
6 files changed, 87 insertions, 96 deletions
diff --git a/docs/api/static.rst b/docs/api/static.rst
index 8ea2fff75..de5bcabda 100644
--- a/docs/api/static.rst
+++ b/docs/api/static.rst
@@ -14,5 +14,3 @@
.. autoclass:: QueryStringCacheBuster
:members:
-
- .. autofunction:: Md5AssetTokenGenerator
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index 642211f5b..7987d03a6 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -370,28 +370,18 @@ equivalent to:
.. code-block:: python
:linenos:
- from pyramid.static import (
- Md5AssetTokenGenerator,
- PathSegmentCacheBuster)
+ from pyramid.static import PathSegmentCacheBuster
# config is an instance of pyramid.config.Configurator
- cachebuster = PathSegmentCacheBuster(Md5AssetTokenGenerator())
config.add_static_view(name='static', path='mypackage:folder/static',
- cachebuster=cachebuster)
+ cachebuster=PathSegmentCacheBuster())
:app:`Pyramid` includes two ready to use cache buster implementations:
:class:`~pyramid.static.PathSegmentCacheBuster`, which inserts an asset token
in the path portion of the asset's URL, and
:class:`~pyramid.static.QueryStringCacheBuster`, which adds an asset token to
-the query string of the asset's URL. Both of these classes have constructors
-which accept a token generator function as an argument, allowing for the way a
-token is generated to be decoupled from the way it is inserted into a URL.
-:app:`Pyramid` provides a single asset token generator,
-:meth:`~pyramid.static.Md5AssetTokenGenerator`.
-
-In order to implement your own cache buster, see the
-:class:`~pyramid.interfaces.ICacheBuster` interface and the existing
-implementations in the :mod:`~pyramid.static` module.
+the query string of the asset's URL. Both of these classes generate md5
+checksums as asset tokens.
.. note::
@@ -400,6 +390,29 @@ implementations in the :mod:`~pyramid.static` module.
:class:`~pyramid.static.PathSegementCacheBuster` to
:class:`~pyramid.static.QueryStringCacheBuster`.
+In order to implement your own cache buster, you can write your own class from
+scratch which implements the :class:`~pyramid.interfaces.ICacheBuster`
+interface. Alternatively you may choose to subclass one of the existing
+implementations. One of the most likely scenarios is you'd want to change the
+way the asset token is generated. To do this just subclass an existing
+implementation and replace the :meth:`~pyramid.interfaces.ICacheBuster.token`
+method. Here is an example which just uses a global setting for the asset
+token:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.static import PathSegmentCacheBuster
+
+ class MyCacheBuster(PathSegmentCacheBuster):
+
+ def __init__(self, config):
+ # config is an instance of pyramid.config.Configurator
+ self._token = config.registry.settings['myapp.cachebust_token']
+
+ def token(self, pathspec):
+ return self._token
+
.. index::
single: static assets view
diff --git a/pyramid/config/views.py b/pyramid/config/views.py
index d74ecfadb..f186a44ae 100644
--- a/pyramid/config/views.py
+++ b/pyramid/config/views.py
@@ -1897,11 +1897,7 @@ def isexception(o):
@implementer(IStaticURLInfo)
class StaticURLInfo(object):
# Indirection for testing
- _default_cachebuster = staticmethod(PathSegmentCacheBuster)
- _default_asset_token_generator = staticmethod(Md5AssetTokenGenerator)
-
- def _make_default_cachebuster(self):
- return self._default_cachebuster(self._default_asset_token_generator())
+ _default_cachebuster = PathSegmentCacheBuster
def _get_registrations(self, registry):
try:
@@ -1964,7 +1960,7 @@ class StaticURLInfo(object):
cb = extra.pop('cachebuster', None)
if cb is True:
- cb = self._make_default_cachebuster()
+ cb = self._default_cachebuster()
if cb:
def cachebuster(subpath, kw):
token = cb.token(spec + subpath)
diff --git a/pyramid/static.py b/pyramid/static.py
index ab9d47aa5..34fc3f55c 100644
--- a/pyramid/static.py
+++ b/pyramid/static.py
@@ -165,16 +165,16 @@ def _generate_md5(spec):
md5.update(block)
return md5.hexdigest()
-def Md5AssetTokenGenerator():
+class Md5AssetTokenGenerator(object):
"""
- A factory method which returns a function that implements
- :meth:`~pyramid.interfaces.ICacheBuster.token`. The function computes and
- returns md5 checksums for static assets, caching them in memory for speedy
- retrieval on subsequent calls.
+ A mixin class which provides an implementation of
+ :meth:`~pyramid.interfaces.ICacheBuster.target` which generates an md5
+ checksum token for an asset, caching it for subsequent calls.
"""
- token_cache = {}
+ def __init__(self):
+ self.token_cache = {}
- def generate_token(pathspec):
+ def token(self, pathspec):
# An astute observer will notice that this use of token_cache doesn't
# look particularly thread safe. Basic read/write operations on Python
# dicts, however, are atomic, so simply accessing and writing values
@@ -187,64 +187,36 @@ def Md5AssetTokenGenerator():
# the extra overhead of using locks to serialize access to the dict
# seems an unnecessary burden.
#
- token = token_cache.get(pathspec)
+ token = self.token_cache.get(pathspec)
if not token:
- token_cache[pathspec] = token = _generate_md5(pathspec)
+ self.token_cache[pathspec] = token = _generate_md5(pathspec)
return token
- return generate_token
-
-class PathSegmentCacheBuster(object):
+class PathSegmentCacheBuster(Md5AssetTokenGenerator):
"""
An implementation of :class:`~pyramid.interfaces.ICacheBuster` which
- inserts a token for cache busting in the path portion of an asset URL.
-
- The ``token`` argument should be an implementation of
- :meth:`~pyramid.interfaces.ICacheBuster.token`. For example, to use
- this cache buster with an md5 token generator:
-
- .. code-block:: python
- :linenos:
-
- from pyramid.static import (
- Md5AssetTokenGenerator,
- PathSegmentCacheBuster)
-
- cachebuster = PathSegmentCacheBuster(Md5AssetTokenGenerator())
+ inserts an md5 checksum token for cache busting in the path portion of an
+ asset URL. Generated md5 checksums are cached in order to speed up
+ subsequent calls.
"""
- def __init__(self, token):
- self.token = token
-
def pregenerate(self, token, subpath, kw):
return (token,) + subpath, kw
def match(self, subpath):
return subpath[1:]
-class QueryStringCacheBuster(object):
+class QueryStringCacheBuster(Md5AssetTokenGenerator):
"""
- An implementation of :class:`~pyramid.interfaces.ICacheBuster` which
- adds a token for cache busting in the query string of an asset URL.
-
- The ``token`` argument should be an implementation of
- :meth:`~pyramid.interfaces.ICacheBuster.token`. For example, to use
- this cache buster with an md5 token generator:
-
- .. code-block:: python
- :linenos:
-
- from pyramid.static import (
- Md5AssetTokenGenerator,
- PathSegmentCacheBuster)
-
- cachebuster = QueryStringCacheBuster(Md5AssetTokenGenerator())
+ An implementation of :class:`~pyramid.interfaces.ICacheBuster` which adds a
+ token for cache busting in the query string of an asset URL. Generated md5
+ checksums are cached in order to speed up subsequent calls.
The optional ``param`` argument determines the name of the parameter added
to the query string and defaults to ``'x'``.
"""
- def __init__(self, token, param='x'):
+ def __init__(self, param='x'):
+ super(QueryStringCacheBuster, self).__init__()
self.param = param
- self.token = token
def pregenerate(self, token, subpath, kw):
query = kw.setdefault('_query', {})
diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py
index 0b81f5a6f..10a2f6f53 100644
--- a/pyramid/tests/test_config/test_views.py
+++ b/pyramid/tests/test_config/test_views.py
@@ -3944,19 +3944,14 @@ class TestStaticURLInfo(unittest.TestCase):
def test_add_cachebust_default(self):
config = self._makeConfig()
inst = self._makeOne()
- inst._default_asset_token_generator = lambda: lambda pathspec: 'foo'
+ inst._default_cachebuster = DummyCacheBuster
inst.add(config, 'view', 'mypackage:path', cachebuster=True)
cachebuster = config.registry._static_url_registrations[0][3]
- subpath, _ = cachebuster('some/path', None)
- self.assertEqual(subpath, 'foo/some/path')
+ subpath, kw = cachebuster('some/path', {})
+ self.assertEqual(subpath, 'some/path')
+ self.assertEqual(kw['x'], 'foo')
def test_add_cachebust_custom(self):
- class DummyCacheBuster(object):
- def token(self, pathspec):
- return 'foo'
- def pregenerate(self, token, subpath, kw):
- kw['x'] = token
- return subpath, kw
config = self._makeConfig()
inst = self._makeOne()
inst.add(config, 'view', 'mypackage:path',
@@ -4071,6 +4066,13 @@ class DummyMultiView:
def __permitted__(self, context, request):
""" """
+class DummyCacheBuster(object):
+ def token(self, pathspec):
+ return 'foo'
+ def pregenerate(self, token, subpath, kw):
+ kw['x'] = token
+ return subpath, kw
+
def parse_httpdate(s):
import datetime
# cannot use %Z, must use literal GMT; Jython honors timezone
diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py
index f7b580df2..6ae9b13db 100644
--- a/pyramid/tests/test_static.py
+++ b/pyramid/tests/test_static.py
@@ -369,29 +369,33 @@ class Test_static_view_use_subpath_True(unittest.TestCase):
self.assertRaises(HTTPNotFound, inst, context, request)
class TestMd5AssetTokenGenerator(unittest.TestCase):
+ _fspath = None
+
+ @property
+ def fspath(self):
+ if self._fspath:
+ return self._fspath
- def setUp(self):
import os
import tempfile
- self.tmp = tempfile.mkdtemp()
- self.fspath = os.path.join(self.tmp, 'test.txt')
-
- def tearDown(self):
import shutil
- shutil.rmtree(self.tmp)
+ tmp = tempfile.mkdtemp()
+ self.addCleanup(lambda: shutil.rmtree(tmp))
+ self._fspath = os.path.join(tmp, 'test.txt')
+ return self._fspath
def _makeOne(self):
- from pyramid.static import Md5AssetTokenGenerator as unit
- return unit()
+ from pyramid.static import Md5AssetTokenGenerator as cls
+ return cls()
def test_package_resource(self):
- fut = self._makeOne()
+ fut = self._makeOne().token
expected = '76d653a3a044e2f4b38bb001d283e3d9'
token = fut('pyramid.tests:fixtures/static/index.html')
self.assertEqual(token, expected)
def test_filesystem_resource(self):
- fut = self._makeOne()
+ fut = self._makeOne().token
expected = 'd5155f250bef0e9923e894dbc713c5dd'
with open(self.fspath, 'w') as f:
f.write("Are we rich yet?")
@@ -399,7 +403,7 @@ class TestMd5AssetTokenGenerator(unittest.TestCase):
self.assertEqual(token, expected)
def test_cache(self):
- fut = self._makeOne()
+ fut = self._makeOne().token
expected = 'd5155f250bef0e9923e894dbc713c5dd'
with open(self.fspath, 'w') as f:
f.write("Are we rich yet?")
@@ -415,8 +419,10 @@ class TestMd5AssetTokenGenerator(unittest.TestCase):
class TestPathSegmentCacheBuster(unittest.TestCase):
def _makeOne(self):
- from pyramid.static import PathSegmentCacheBuster as unit
- return unit(lambda pathspec: 'foo')
+ from pyramid.static import PathSegmentCacheBuster as cls
+ inst = cls()
+ inst.token = lambda pathspec: 'foo'
+ return inst
def test_token(self):
fut = self._makeOne().token
@@ -432,9 +438,14 @@ class TestPathSegmentCacheBuster(unittest.TestCase):
class TestQueryStringCacheBuster(unittest.TestCase):
- def _makeOne(self):
- from pyramid.static import QueryStringCacheBuster as unit
- return unit(lambda pathspec: 'foo')
+ def _makeOne(self, param=None):
+ from pyramid.static import QueryStringCacheBuster as cls
+ if param:
+ inst = cls(param)
+ else:
+ inst = cls()
+ inst.token = lambda pathspec: 'foo'
+ return inst
def test_token(self):
fut = self._makeOne().token
@@ -447,8 +458,7 @@ class TestQueryStringCacheBuster(unittest.TestCase):
(('bar',), {'_query': {'x': 'foo'}}))
def test_pregenerate_change_param(self):
- from pyramid.static import QueryStringCacheBuster as unit
- fut = unit(lambda pathspec: 'foo', 'y').pregenerate
+ fut = self._makeOne('y').pregenerate
self.assertEqual(
fut('foo', ('bar',), {}),
(('bar',), {'_query': {'y': 'foo'}}))