Package: plone.caching
Table of ContentsIntroductionUsageDeclaring cache rules for a viewMapping cache rules to operationsSetting options for caching operationsWriting caching operationsChangelog2.0.1 (2023-10-07)2.0.0 (2023-04-15)1.2.2 (2020-04-20)1.2.1 (2018-11-29)1.2.0 (2018-09-28)1.1.2 (2016-09-16)1.1.1 (2016-08-12)1.1.0 (2016-05-18)1.0.1 (2015-03-21)1.0 - 2011-05-131.0b2 - 2011-02-101.0b1 - 2010-08-041.0a1 - 2010-04-22IntroductionTheplone.cachingpackage provides a framework for the management of cache headers, built
atopz3c.caching. It consists of the following elements:An interfaceICachingOperation, 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 interfacesICachingOperationTypewhich 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 byplone.registryAn operation calledplone.caching.operations.chain, which can be used to
chain together multiple operations. It will look up the optionplone.caching.operations.chain.${rulename}.operationsin 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).UsageTo useplone.caching, you must first install it into your build and load
its configuration. If you are using Plone, you can do that by installingplone.app.caching. Otherwise, depend onplone.cachingin your
own package’ssetup.py:install_requires = [
    ...
    'plone.caching',
    ]Then load the package’s configuration from your own package’sconfigure.zcml:<include package="plone.caching" />Next, you must ensure that the the cache settings records are installed in
the registry. (plone.cachingusesplone.registryto store various
settings, and provides helpers for caching operations to do the same.)To use the registry, you must register a (usually local) utility providingplone.registry.interfaces.IRegistry. If you are using Plone, installingplone.app.registrywill do this for you. Otherwise, configure one manually
using thezope.componentAPI.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 theplone.cachingsettings to the registry. If you useplone.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 valueplone.caching.interfaces.ICacheSettings.enabledtoTrue.
If you are using Plone and have installedplone.app.caching, you can do
this from the caching control panel. In code, you can do:registry['plone.caching.interfaces.ICacheSettings.enabled'] = TrueDeclaring cache rules for a viewThe entry point for caching is acache 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 atz3c.cachingfor 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 classFrontpageViewis associated with
the rule setplone.contentTypes.NOTE:Ruleset names should bedotted 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) theplone.contentTypesruleset should be
declared with a title and description. This is can be used by a UI such as
that provided byplone.app.caching. If “explicit” mode is set inz3c.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 anoverrides.zcmlfile for your project.Mapping cache rules to operationsplone.cachingmaintains 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 recordplone.caching.interfaces.ICacheSettings.operationMapping.To set the name of the operation to use for theplone.contentTypesrule
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.operationis 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 theplone.caching.operations.chainoperation: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 theoperationsoption for the chain
operation, in a way that is specific to theplone.contentTypesrule 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 theICachingOperationTypeutility:from zope.component import getUtilitiesFor
from plone.caching.interfaces import ICachingOperationType

for name, type_ in getUtilitiesFor(ICachingOperationType):
    ...TheICachingOperationTypeutility provides properties liketitleanddescriptionto help build a user interface around caching operations.plone.app.cachingprovides just such an interface.Setting options for caching operationsplone.cachingdoes not strictly enforce how caching operations configure
themselves, if at all. However, it provides helper functionality to encourage
a pattern based on settings stored inplone.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 classplone.caching.operations.Chain. TheICachingOperationTypeutility
namedplone.caching.operations.chainprovides two attributes in addition
to thetitleanddescriptionattributes mentioned above:prefixA dotted name prefix used for all registry keys. This key must be unique.
By convention, it is the name of the caching operationoptionsA tuple of option namesTaken 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 calledmy.package.operation, the prefix is the same
string, and the options are('option1', 'option2'), two registry keys
will be used:my.package.operation.option1andmy.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 rulemy.rule, the rule-specific version ofoption1would bemy.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
theplone.registrydocumentation 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 operationsNow that we have seen how to configure cache rules and operations, let’s look
at how we can write our own caching operationsCaching operations consist of two components:A named multi-adapter implementing the operation itselfA named utility providing metadata about the operationTypically, 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 byplone.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 thelookupOptions()helper method. You can pass this
either anICachingOperationTypeinstance, 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 (onlymaxAgein this case), taking rule set overrides into account. The
options are guaranteed to be there, but will fall back on a default ofNoneif 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 usingcomponentinstead offactoryin 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 theinterceptResponse()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):
        passHere, we returnNoneto indicate that the request should not be
intercepted if thetemporarilyDisableoption is set toTrue.
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" />Changelog2.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-13Release 1.0 Final.
[esteele]Add MANIFEST.in.
[WouterVH]1.0b2 - 2011-02-10Updated 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-04Preparing release to coincide with plone.app.caching 1.0b1
[optilude]1.0a1 - 2010-04-22Initial release
[optilude, newbery]