Zope 2 integration for z3c.caching
Project description
Introduction
The plone.caching package provides a framework for the management of cache headers, built atop z3c.caching. It consists of the following elements:
- An interface ICachingOperation, describing components which:
Modify the response for caching purposes. The most common operation will be to set cache headers.
Intercept a request before view rendering (but after traversal and authorisation) to provide a cached response. The most common operation will be to set a “304 Not Modified” response header and return an empty response, although it is also possible to provide a full response body.
Caching operations are named multi-adapters on the published object (e.g. a view) and the request.
An interfaces ICachingOperationType which is used for utilities describing caching operations. This is mainly for UI purposes, although this package does not provide any UI of its own.
Hooks into the Zope 2 ZPublisher (installed provided ZPublisher is available) which will execute caching operations as appropriate.
Helper functions for looking up configuration options caching operations in a registry managed by plone.registry
An operation called plone.caching.operations.chain, which can be used to chain together multiple operations. It will look up the option plone.caching.operations.chain.${rulename}.operations in the registry, expecting a list of strings indicating the names of operations to execute. (${rulename} refers to the name of the caching rule set in use - more on this later).
Usage
To use plone.caching, you must first install it into your build and load its configuration. If you are using Plone, you can do that by installing plone.app.caching. Otherwise, depend on plone.caching in your own package’s setup.py:
install_requires = [ ... 'plone.caching', ]
Then load the package’s configuration from your own package’s configure.zcml:
<include package="plone.caching" />
Next, you must ensure that the the cache settings records are installed in the registry. (plone.caching uses plone.registry to store various settings, and provides helpers for caching operations to do the same.)
To use the registry, you must register a (usually local) utility providing plone.registry.interfaces.IRegistry. If you are using Plone, installing plone.app.registry will do this for you. Otherwise, configure one manually using the zope.component API.
In tests, you can do the following:
from zope.component import provideAdapter from plone.registry.interfaces import IRegistry from plone.registry import Registry provideAdapter(Registry(), IRegistry)
Next, you must add the plone.caching settings to the registry. If you use plone.app.caching, it will do this for you. Otherwise, you can register them like so:
from zope.component import getUtility from plone.registry.interfaces import IRegistry from plone.caching.interfaces import ICacheSettings registry = getUtility(IRegistry) registry.registerInterface(ICacheSettings)
Finally, you must turn on the caching engine, by setting the registry value plone.caching.interfaces.ICacheSettings.enabled to True. If you are using Plone and have installed plone.app.caching, you can do this from the caching control panel. In code, you can do:
registry['plone.caching.interfaces.ICacheSettings.enabled'] = True
Declaring cache rules for a view
The entry point for caching is a cache rule set. A rule set is simply a name given to a collection of publishable resources, such as views, for caching purposes. Take a look at z3c.caching for details, but a simple example may look like this:
<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:cache="http://namespaces.zope.org/cache"> <cache:ruleset for=".frontpage.FrontpageView" ruleset="plone.contentTypes" /> <browser:page for="..interfaces.IFrontpage" class=".frontpage.FrontpageView" name="frontpage_view" template="templates/frontpage_view.pt" permission="zope2.View" /> </configure>
Here, the view implemented by the class FrontpageView is associated with the rule set plone.contentTypes.
NOTE: Ruleset names should be dotted names. That is, they should consist only of upper or lowercase letters, digits, underscores and/or periods (dots). The idea is that this forms a namespace similar to namespaces created by packages and modules in Python.
Elsewhere (or in the same file) the plone.contentTypes ruleset should be declared with a title and description. This is can be used by a UI such as that provided by plone.app.caching. If “explicit” mode is set in z3c.caching, this is required. By default it is optional:
<cache:rulesetType name="plone.contentTypes" title="Plone content types" description="Non-container content types" />
Hints:
Try to reuse existing rule sets rather than invent your own.
Rule sets inherit according to the same rules as those that apply to adapters. Thus, you can register a generic rule set for a generic interface or base class, and then override it for a more specific class or interface.
If you need to modify rule sets declared by packages not under your control, you can use an overrides.zcml file for your project.
Mapping cache rules to operations
plone.caching maintains a mapping of rule sets to caching operations in the registry. This mapping is stored in a dictionary of dotted name string keys to dotted name string values, under the record plone.caching.interfaces.ICacheSettings.operationMapping.
To set the name of the operation to use for the plone.contentTypes rule shown above, a mapping like the following might be used:
from zope.component import getUtility from plone.registry.interfaces import IRegistry from plone.caching.interfaces import ICacheSettings registry = getUtility(IRegistry) settings = registry.forInterface(ICacheSettings) if settings.operationMapping is None: # initialise if not set already settings.operationMapping = {} settings.operationMapping['plone.contentTypes'] = 'my.package.operation'
Here, my.package.operation is the name of a caching operation. We will see an example of using one shortly.
If you want to use several operations, you can chain them together using the plone.caching.operations.chain operation:
settings.operationMapping['plone.contentTypes'] = 'plone.caching.operations.chain' registry['plone.caching.operations.chain.plone.contentTypes.operations'] = \ ['my.package.operation1', 'my.package.operation2']
The last line here is setting the operations option for the chain operation, in a way that is specific to the plone.contentTypes rule set. More on the configuration syntax shortly.
If you need to list all operations for UI purposes, you can look up the registered instances of the ICachingOperationType utility:
from zope.component import getUtilitiesFor from plone.caching.interfaces import ICachingOperationType for name, type_ in getUtilitiesFor(ICachingOperationType): ...
The ICachingOperationType utility provides properties like title and description to help build a user interface around caching operations. plone.app.caching provides just such an interface.
Setting options for caching operations
plone.caching does not strictly enforce how caching operations configure themselves, if at all. However, it provides helper functionality to encourage a pattern based on settings stored in plone.registry. We have already seen this pattern in use for the chain operation above. Let’s now take a closer look.
The chain operation is implemented by the class plone.caching.operations.Chain. The ICachingOperationType utility named plone.caching.operations.chain provides two attributes in addition to the title and description attributes mentioned above:
- prefix
A dotted name prefix used for all registry keys. This key must be unique. By convention, it is the name of the caching operation
- options
A tuple of option names
Taken together, these attributes describe the configurable options (if any) of the caching operation. By default, the two are concatenated, so that if you have an operation called my.package.operation, the prefix is the same string, and the options are ('option1', 'option2'), two registry keys will be used: my.package.operation.option1 and my.package.operation.option2. (The type of those records and their value will obviously depend on how the registry is configured. Typically, the installation routine for a given operation will create them with sensible defaults).
If you need to change these settings on a per-cache-rule basis, you can do so by inserting the cache rule name between the prefix and the option name. For example, for the cache rule my.rule, the rule-specific version of option1 would be my.package.operation.my.rule.option1.
In this case, you probably want to use a field reference (FieldRef) for the “override” record that references the field of the “base” record. See the plone.registry documentation for details.
Finally, note that it is generally safe to use caching operations if their registry keys are not installed. That is, they should fall back on sensible defaults and not crash.
Writing caching operations
Now that we have seen how to configure cache rules and operations, let’s look at how we can write our own caching operations
Caching operations consist of two components:
A named multi-adapter implementing the operation itself
A named utility providing metadata about the operation
Typically, both of these are implemented within a single class, although this is not a requirement. Typically, the operation will also look up options in accordance with the configuration methodology outlines above.
Here is an example of an operation that sets a fixed max-age cache control header. It is registered for any published resource, and for any HTTP request (but not other types of request.):
from plone.caching.interfaces import _ from plone.caching.interfaces import ICachingOperation from plone.caching.interfaces import ICachingOperationType from plone.caching.utils import lookupOptions from zope.component import adapter from zope.component import queryMultiAdapter from zope.interface import implementer from zope.interface import Interface from zope.interface import provider from zope.publisher.interfaces.http import IHTTPRequest @implementer(ICachingOperation) @adapter(Interface, IHTTPRequest) @provider(ICachingOperationType) class MaxAge(object): # Type metadata title = _(u"Max age") description = _(u"Sets a fixed max age value") prefix = 'plone.caching.tests.maxage' options = ('maxAge',) def __init__(self, published, request): self.published = published self.request = request def interceptResponse(self, rulename, response): return None def modifyResponse(self, rulename, response): options = lookupOptions(MaxAge, rulename) maxAge = options['maxAge'] or 3600 response.setHeader('Cache-Control', 'max-age=%s, must-revalidate' % maxAge)
There are two methods here:
interceptResponse() is called before Zope attempts to render the published object. If this returns None, publication continues as normal. If it returns a string, the request is intercepted and the cached response is returned.
modifyResponse() is called after Zope has rendered the response (in a late stage of the transformation chain set up by plone.transformchain). This should not return a value, but can modify the response passed in. It should not modify the response body (in fact, doing so will have on effect), but may set headers.
Note the use of the lookupOptions() helper method. You can pass this either an ICachingOperationType instance, or the name of one (in which case it will be looked up from the utility registry), as well as the current rule name. It will return a dictionary of all the options listed (only maxAge in this case), taking rule set overrides into account. The options are guaranteed to be there, but will fall back on a default of None if not set.
To register this component in ZCML, we would do:
<adapter factory=".maxage.MaxAge" name="plone.caching.tests.maxage" /> <utility component=".maxage.MaxAge" name="plone.caching.tests.maxage" />
Note that by using component instead of factory in the <utility /> declaration, we register the class object itself as the utility. The attributes are provided as class variables for that reason - setting them in __init__(), for example, would not work.
What about the interceptResponse() method? Here is a simple example that sends a 304 not modified response always. (This is probably not very useful, but it serves as an example.):
from plone.caching.interfaces import _ from plone.caching.interfaces import ICachingOperation from plone.caching.interfaces import ICachingOperationType from plone.caching.utils import lookupOptions from zope.component import adapter from zope.component import queryMultiAdapter from zope.interface import implementer from zope.interface import Interface from zope.interface import provider from zope.publisher.interfaces.http import IHTTPRequest @implementer(ICachingOperation) @adapter(Interface, IHTTPRequest) @provider(ICachingOperationType) class Always304(object): # Type metadata title = _(u"Always send 304") description = _(u"It's not modified, dammit!") prefix = 'plone.caching.tests.always304' options = ('temporarilyDisable',) def __init__(self, published, request): self.published = published self.request = request def interceptResponse(self, rulename, response): options = lookupOptions(self.__class__, rulename) if options['temporarilyDisable']: return None response.setStatus(304) return u"" def modifyResponse(self, rulename, response): pass
Here, we return None to indicate that the request should not be intercepted if the temporarilyDisable option is set to True. Otherwise, we modify the response and return a response body. The return value must be a unicode string. In this case, an empty string will suffice.
The ZCML registration would look like this:
<adapter factory=".always.Always304" name="plone.caching.tests.always304" /> <utility component=".always.Always304" name="plone.caching.tests.always304" />
Changelog
2.0.1 (2023-10-07)
Internal:
Update configuration files. [plone devs] (cfffba8c)
2.0.0 (2023-04-15)
Breaking changes:
Drop python 2 support. [gforcada] (#1)
Internal:
Update configuration files. [plone devs] (3b8337e6)
1.2.2 (2020-04-20)
Bug fixes:
Minor packaging updates. (#1)
1.2.1 (2018-11-29)
Bug fixes:
Remove five.globalrequest usage. [gforcada]
1.2.0 (2018-09-28)
New features:
Add support for Python 3. [pbauer]
Bug fixes:
Fix caching and tests in python 3 [ale-rt, pbauer]
1.1.2 (2016-09-16)
Bug fixes:
Cleanup: isort, readability, pep8, utf8-headers. [jensens]
1.1.1 (2016-08-12)
Bug fixes:
Use zope.interface decorator. [gforcada]
1.1.0 (2016-05-18)
Fixes:
Use plone i18n domain. [klinger]
1.0.1 (2015-03-21)
Fix ruleset registry test isolation so that is no longer order dependent. [jone]
1.0 - 2011-05-13
Release 1.0 Final. [esteele]
Add MANIFEST.in. [WouterVH]
1.0b2 - 2011-02-10
Updated tests to reflect operation parameter overrides can now use plone.registry FieldRefs. Requires plone.registry >= 1.0b3. [optilude]
Removed monkey patches unneeded since Zope 2.12.4. [optilude]
1.0b1 - 2010-08-04
Preparing release to coincide with plone.app.caching 1.0b1 [optilude]
1.0a1 - 2010-04-22
Initial release [optilude, newbery]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file plone.caching-2.0.1.tar.gz
.
File metadata
- Download URL: plone.caching-2.0.1.tar.gz
- Upload date:
- Size: 30.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f4bdd1b2816f3e2257e9727a6287965898cd2f3b3cde1ee12d4fc75a93512e6f |
|
MD5 | 560cce85426db4b1ee673984f8728565 |
|
BLAKE2b-256 | 6b33430b128b194310baafabf31e4730e9e16b87b468fc34c13e1222216f4fbd |
File details
Details for the file plone.caching-2.0.1-py3-none-any.whl
.
File metadata
- Download URL: plone.caching-2.0.1-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.11.5
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | caba0f9aa46d11f475e4d65199681e9e47e56381f25058b0c925e59ea7f17182 |
|
MD5 | 17a883e5660daf1ad2fc1dbb96ba3ef0 |
|
BLAKE2b-256 | bf50357769a5d3347a49500a62b0d3fd82a75acb79d80644af72f5f00d52ed61 |