From 2b9d281e3e4e11a91adb1715f036a153f5fa9e1b Mon Sep 17 00:00:00 2001 From: Asko Soukka Date: Mon, 15 Apr 2019 19:07:09 +0300 Subject: [PATCH 1/3] Fix issue where layout aware tile data storage read cache was not purged when data was updated programmatically [fixes #75] --- plone/app/blocks/layoutbehavior.py | 56 +++++++++++++++++++ plone/app/blocks/tests/test_layoutbehavior.py | 2 - 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/plone/app/blocks/layoutbehavior.py b/plone/app/blocks/layoutbehavior.py index 57a43950..38d47d86 100644 --- a/plone/app/blocks/layoutbehavior.py +++ b/plone/app/blocks/layoutbehavior.py @@ -33,6 +33,7 @@ from repoze.xmliter.utils import getHTMLSerializer from zExceptions import NotFound from zope import schema +from zope.annotation.interfaces import IAnnotations from zope.component import adapter from zope.component import getUtility from zope.component import queryUtility @@ -299,6 +300,34 @@ def layoutAwareTileDataStorage(context, request, tile): return defaultTileDataStorage(context, request, tile) +def invalidate_view_memoize(view, func, args, kwargs): + """Invalidate @view.memoize for given view, func, args and kwargs. + + See: plone/memoize/view.py + """ + + context = view.context + annotations = IAnnotations(view.request, None) or {} + cache = annotations.get('plone.memoize') + + if cache: + try: + context_id = context.getPhysicalPath() + except AttributeError: + context_id = id(context) + + # Note: we don't use args[0] in the cache key, since args[0] == + # view instance and the whole point is that we can cache different + # requests + + key = (context_id, view.__class__.__name__, func.__name__, + args[1:], frozenset(kwargs.items())) + + return cache.pop(key, None) + else: + return None + + @implementer(ITileDataStorage) @adapter(ILayoutBehaviorAdaptable, Interface, ITile) class LayoutAwareTileDataStorage(object): @@ -388,6 +417,15 @@ def __delitem__(self, key): for el in self.storage.tree.xpath( '//*[contains(@data-tile, "{0:s}")]'.format(key)): el.remove() + + # Purge view.memoize + invalidate_view_memoize( + self, self.__getitem__, (self, key), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.lstrip('@')), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + return self.sync() raise KeyError(key) @@ -421,6 +459,15 @@ def __setitem__(self, key, value): del el.attrib['data-tiledata'] if primary is not None: el.append(primary) + + # Purge view.memoize + invalidate_view_memoize( + self, self.__getitem__, (self, key), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.lstrip('@')), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + return self.sync() # Add new value @@ -431,6 +478,15 @@ def __setitem__(self, key, value): if primary is not None: el.append(primary) self.storage.tree.find('body').append(el) + + # Purge view.memoize + invalidate_view_memoize( + self, self.__getitem__, (self, key), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.lstrip('@')), {}) + invalidate_view_memoize( + self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + self.sync() # IEnumerableMapping diff --git a/plone/app/blocks/tests/test_layoutbehavior.py b/plone/app/blocks/tests/test_layoutbehavior.py index 9b26903f..d1ec1c99 100644 --- a/plone/app/blocks/tests/test_layoutbehavior.py +++ b/plone/app/blocks/tests/test_layoutbehavior.py @@ -96,8 +96,6 @@ def test_content(self): data['message'] = u"Foo bar!" storage[tile] = data - delattr(self.layer['request'], '__annotations__') # purge memoize - view = self.portal['f1']['d1'].restrictedTraverse(tile)() self.assertEqual( view, From 9ca8007f169987852eca0674d2d962094d246b2d Mon Sep 17 00:00:00 2001 From: Asko Soukka Date: Mon, 15 Apr 2019 19:07:43 +0300 Subject: [PATCH 2/3] Update changelog --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6f3079ce..8ea64eff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,11 @@ Changelog 4.3.2 (unreleased) ------------------ -- Nothing changed yet. +Bug fixes: +- Fix issue where layout aware tile data storage read cache was not purged when + data was updated programmatically [fixes #75] + [datakurre] 4.3.1 (2019-02-20) ------------------ From af40788d0d3fe17f5483903b48c977019a076677 Mon Sep 17 00:00:00 2001 From: Asko Soukka Date: Mon, 15 Apr 2019 20:12:30 +0300 Subject: [PATCH 3/3] Change memoize invalidation to require function name instead of function to fix issue where Plone 4.3 versions of view.memoize masked function name --- plone/app/blocks/layoutbehavior.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plone/app/blocks/layoutbehavior.py b/plone/app/blocks/layoutbehavior.py index 38d47d86..1fe118b9 100644 --- a/plone/app/blocks/layoutbehavior.py +++ b/plone/app/blocks/layoutbehavior.py @@ -300,8 +300,8 @@ def layoutAwareTileDataStorage(context, request, tile): return defaultTileDataStorage(context, request, tile) -def invalidate_view_memoize(view, func, args, kwargs): - """Invalidate @view.memoize for given view, func, args and kwargs. +def invalidate_view_memoize(view, name, args, kwargs): + """Invalidate @view.memoize for given view, function name, args and kwargs. See: plone/memoize/view.py """ @@ -320,7 +320,7 @@ def invalidate_view_memoize(view, func, args, kwargs): # view instance and the whole point is that we can cache different # requests - key = (context_id, view.__class__.__name__, func.__name__, + key = (context_id, view.__class__.__name__, name, args[1:], frozenset(kwargs.items())) return cache.pop(key, None) @@ -420,11 +420,11 @@ def __delitem__(self, key): # Purge view.memoize invalidate_view_memoize( - self, self.__getitem__, (self, key), {}) + self, '__getitem__', (self, key), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.lstrip('@')), {}) + self, '__getitem__', (self, key.lstrip('@')), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + self, '__getitem__', (self, key.split('/', 1)[-1]), {}) return self.sync() raise KeyError(key) @@ -462,11 +462,11 @@ def __setitem__(self, key, value): # Purge view.memoize invalidate_view_memoize( - self, self.__getitem__, (self, key), {}) + self, '__getitem__', (self, key), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.lstrip('@')), {}) + self, '__getitem__', (self, key.lstrip('@')), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + self, '__getitem__', (self, key.split('/', 1)[-1]), {}) return self.sync() @@ -481,11 +481,11 @@ def __setitem__(self, key, value): # Purge view.memoize invalidate_view_memoize( - self, self.__getitem__, (self, key), {}) + self, '__getitem__', (self, key), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.lstrip('@')), {}) + self, '__getitem__', (self, key.lstrip('@')), {}) invalidate_view_memoize( - self, self.__getitem__, (self, key.split('/', 1)[-1]), {}) + self, '__getitem__', (self, key.split('/', 1)[-1]), {}) self.sync()