plone: removing (portlet)renderers from the cache


I have a testing setup which runs some tests on a live site. This lead to strange errors when running specific tests at the same time. Caching is the problem.


For a customer I need to do lot's of checks on the live site. Because of reasons(TM) I have a browser view as a test runner, which then runs several of the tests. The tests ran fine indiviually, but when running some of them in the same request, I got the following issues error message:

2015-06-02 16:21:27 ERROR Zope.SiteErrorLog 1433254887.110.276035955441 http://localhost/p8/batests
Traceback (innermost last):
 Module plone.app.portlets.manager, line 63, in safe_render
 Module plone.memoize.volatile, line 276, in replacement
 Module plone.app.portlets.portlets.recent, line 40, in _render_cachekey
 Module plone.app.portlets.cache, line 29, in render_cachekey
 Module plone.app.portlets.cache, line 27, in add
 Module Products.ZCatalog.CatalogBrains, line 51, in getPath
 Module Products.ZCatalog.ZCatalog, line 518, in getpath
KeyError: -1639031583

I create some stuff in the first test, and render a page. This leads to the recent items portlet to be rendered, and cached. Then I delete the stuff, and run the second test. Now the page including the portlet gets rendered again, and this causes the error - because the object is deleted, but still in some cache.

I first tried (a lot) to find ways to get hold of the PortletRenderer, which actually has the cache, but this didn't lead anywhere - inoking the portlet adapter magic machine didn't work as expected.


Finally (did I mention it: after lots of trying) I found that even though I can't delete the caches on the Renderers, the renderers themself are memoized. And they are stored in an annotation in the request object. So here is a little hack to get rid of the cached Renderers (or everything, as needed):


def cleanPortletCaches(content,selection=None):
    clean everything that causes catalog errors from the cache. This is a hack!
    request = content.REQUEST
    if not (hasattr(request,'__annotations__') and request.__annotations__.has_key('plone.memoize')):
    cache = request.__annotations__['plone.memoize']
    for key in [k for k in cache.keys() if 'ColumnPortletManagerRenderer' in str(k)]:
        # die grobschlaechtige Version:
        if selection is None:
            value = cache[key]
            newvalue  = [entry for entry in value if entry['name'] not in selection]
            cache[key] = newvalue

def cleanAllCaches(content):
    request = content.REQUEST
    if not (hasattr(request,'__annotations__') and request.__annotations__.has_key('plone.memoize')):
    del request.__annotations__['plone.memoize']