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.
Solution
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')):
return
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:
del(cache[key])
else:
value = cache[key]
newvalue = [entry for entry in value if entry['name'] not in selection]
cache[key] = newvalue
return
def cleanAllCaches(content):
request = content.REQUEST
if not (hasattr(request,'__annotations__') and request.__annotations__.has_key('plone.memoize')):
return
del request.__annotations__['plone.memoize']