Commit d9b010c4 authored by Hanno Schlichting's avatar Hanno Schlichting

Revert "Remove ZCacheable logic and StandardCacheManagers dependency."

This reverts part of commit dbb476e6.
parent b43972fe
......@@ -27,6 +27,8 @@ Features Added
Restructuring
+++++++++++++
- Add back ZCacheable support.
- Update to zope.testbrowser 5.0 and its WebTest based implementation.
- Use `@implementer` and `@adapter` class decorators.
......
This diff is collapsed.
......@@ -60,6 +60,7 @@ class DTMLDocument(PropertyManager, DTMLMethod):
file = file.read()
self.munge(file)
self.ZCacheable_invalidate()
if REQUEST:
message = "Content uploaded."
return self.manage_main(self, REQUEST, manage_tabs_message=message)
......@@ -69,6 +70,12 @@ class DTMLDocument(PropertyManager, DTMLMethod):
o If supplied, use REQUEST mapping, Response, and key word arguments.
"""
if not self._cache_namespace_keys:
data = self.ZCacheable_get(default=_marker)
if data is not _marker:
# Return cached results.
return data
__traceback_supplement__ = (PathTracebackSupplement, self)
kw['document_id'] = self.getId()
kw['document_title'] = self.title
......@@ -88,11 +95,15 @@ class DTMLDocument(PropertyManager, DTMLMethod):
result = r
else:
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result
r = HTML.__call__(self, (client, bself), REQUEST, **kw)
if RESPONSE is None or not isinstance(r, str):
if not self._cache_namespace_keys:
self.ZCacheable_set(r)
return r
finally:
......@@ -106,6 +117,8 @@ class DTMLDocument(PropertyManager, DTMLMethod):
c, e = guess_content_type(self.__name__, r)
RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result
......
......@@ -37,6 +37,8 @@ from OFS import bbb
from OFS.Cache import Cacheable
from OFS.role import RoleManager
from OFS.SimpleItem import Item_w__name__
from ZPublisher.Iterators import IStreamIterator
if sys.version_info >= (3, ):
basestring = str
......@@ -59,6 +61,7 @@ class DTMLMethod(RestrictedDTML,
"""
meta_type = 'DTML Method'
index_html = None # Prevent accidental acquisition
_cache_namespace_keys = ()
security = ClassSecurityInfo()
security.declareObjectProtected(View)
......@@ -72,7 +75,8 @@ class DTMLMethod(RestrictedDTML,
{'label': 'Edit', 'action': 'manage_main'},
) +
RoleManager.manage_options +
Item_w__name__.manage_options
Item_w__name__.manage_options +
Cacheable.manage_options
)
# More reasonable default for content-type for http HEAD requests.
......@@ -88,6 +92,27 @@ class DTMLMethod(RestrictedDTML,
o If supplied, use the REQUEST mapping, Response, and key word
arguments.
"""
if not self._cache_namespace_keys:
data = self.ZCacheable_get(default=_marker)
if data is not _marker:
if (IStreamIterator.isImplementedBy(data) and
RESPONSE is not None):
# This is a stream iterator and we need to set some
# headers now before giving it to medusa
headers_get = RESPONSE.headers.get
if headers_get('content-length', None) is None:
RESPONSE.setHeader('content-length', len(data))
if (headers_get('content-type', None) is None and
headers_get('Content-type', None) is None):
ct = (self.__dict__.get('content_type') or
self.default_content_type)
RESPONSE.setHeader('content-type', ct)
# Return cached results.
return data
__traceback_supplement__ = (PathTracebackSupplement, self)
kw['document_id'] = self.getId()
kw['document_title'] = self.title
......@@ -108,10 +133,14 @@ class DTMLMethod(RestrictedDTML,
result = r
else:
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result
r = HTML.__call__(self, client, REQUEST, **kw)
if RESPONSE is None or not isinstance(r, str):
if not self._cache_namespace_keys:
self.ZCacheable_set(r)
return r
finally:
......@@ -127,24 +156,54 @@ class DTMLMethod(RestrictedDTML,
c, e = guess_content_type(self.getId(), r)
RESPONSE.setHeader('Content-Type', c)
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
return result
def validate(self, inst, parent, name, value, md=None):
return getSecurityManager().validate(inst, parent, name, value)
def ZDocumentTemplate_beforeRender(self, md, default):
# Tries to get a cached value.
if self._cache_namespace_keys:
# Use the specified keys from the namespace to identify a
# cache entry.
kw = {}
for key in self._cache_namespace_keys:
try:
val = md[key]
except:
val = None
kw[key] = val
return self.ZCacheable_get(keywords=kw, default=default)
return default
def ZDocumentTemplate_afterRender(self, md, result):
pass
# Tries to set a cache value.
if self._cache_namespace_keys:
kw = {}
for key in self._cache_namespace_keys:
try:
val = md[key]
except:
val = None
kw[key] = val
self.ZCacheable_set(result, keywords=kw)
security.declareProtected(change_dtml_methods, 'getCacheNamespaceKeys')
def getCacheNamespaceKeys(self):
return ()
# Return the cacheNamespaceKeys.
return self._cache_namespace_keys
security.declareProtected(change_dtml_methods, 'setCacheNamespaceKeys')
def setCacheNamespaceKeys(self, keys, REQUEST=None):
pass
# Set the list of names looked up to provide a cache key.
ks = []
for key in keys:
key = str(key).strip()
if key:
ks.append(key)
self._cache_namespace_keys = tuple(ks)
security.declareProtected(View, 'get_size')
def get_size(self):
......@@ -178,6 +237,7 @@ class DTMLMethod(RestrictedDTML,
if not isinstance(data, basestring):
data = data.read()
self.munge(data)
self.ZCacheable_invalidate()
if REQUEST:
message = "Saved changes."
return self.manage_main(self, REQUEST, manage_tabs_message=message)
......@@ -195,6 +255,7 @@ class DTMLMethod(RestrictedDTML,
file = file.read()
self.munge(file)
self.ZCacheable_invalidate()
if REQUEST:
message = "Saved changes."
return self.manage_main(self, REQUEST, manage_tabs_message=message)
......@@ -220,6 +281,7 @@ class DTMLMethod(RestrictedDTML,
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '')
self.munge(body)
self.ZCacheable_invalidate()
RESPONSE.setStatus(204)
return RESPONSE
......
......@@ -112,7 +112,8 @@ class File(Persistent, Implicit, PropertyManager,
manage_options = (
({'label': 'Edit', 'action': 'manage_main'}, ) +
RoleManager.manage_options +
Item_w__name__.manage_options
Item_w__name__.manage_options +
Cacheable.manage_options
)
_properties = (
......@@ -372,6 +373,13 @@ class File(Persistent, Implicit, PropertyManager,
"""
if self._if_modified_since_request_handler(REQUEST, RESPONSE):
# we were able to handle this by returning a 304
# unfortunately, because the HTTP cache manager uses the cache
# API, and because 304 responses are required to carry the Expires
# header for HTTP/1.1, we need to call ZCacheable_set here.
# This is nonsensical for caches other than the HTTP cache manager
# unfortunately.
self.ZCacheable_set(None)
return ''
if self.precondition and hasattr(self, str(self.precondition)):
......@@ -393,6 +401,17 @@ class File(Persistent, Implicit, PropertyManager,
RESPONSE.setHeader('Content-Length', self.size)
RESPONSE.setHeader('Accept-Ranges', 'bytes')
if self.ZCacheable_isCachingEnabled():
result = self.ZCacheable_get(default=None)
if result is not None:
# We will always get None from RAMCacheManager and HTTP
# Accelerated Cache Manager but we will get
# something implementing the IStreamIterator interface
# from a "FileCacheManager"
return result
self.ZCacheable_set(None)
data = self.data
if isinstance(data, str):
RESPONSE.setBase(None)
......@@ -430,6 +449,8 @@ class File(Persistent, Implicit, PropertyManager,
size = len(data)
self.size = size
self.data = data
self.ZCacheable_invalidate()
self.ZCacheable_set(None)
self.http__refreshEtag()
security.declareProtected(change_images_and_files, 'manage_edit')
......@@ -449,6 +470,8 @@ class File(Persistent, Implicit, PropertyManager,
del self.precondition
if filedata is not None:
self.update_data(filedata, content_type, len(filedata))
else:
self.ZCacheable_invalidate()
notify(ObjectModifiedEvent(self))
......@@ -609,6 +632,18 @@ class File(Persistent, Implicit, PropertyManager,
def manage_FTPget(self):
"""Return body for ftp."""
RESPONSE = self.REQUEST.RESPONSE
if self.ZCacheable_isCachingEnabled():
result = self.ZCacheable_get(default=None)
if result is not None:
# We will always get None from RAMCacheManager but we will
# get something implementing the IStreamIterator interface
# from FileCacheManager.
# the content-length is required here by HTTPResponse,
# even though FTP doesn't use it.
RESPONSE.setHeader('Content-Length', self.size)
return result
data = self.data
if isinstance(data, str):
RESPONSE.setBase(None)
......@@ -765,7 +800,8 @@ class Image(File):
manage_options = (
({'label': 'Edit', 'action': 'manage_main'}, ) +
RoleManager.manage_options +
Item_w__name__.manage_options
Item_w__name__.manage_options +
Cacheable.manage_options
)
manage_editForm = DTMLFile('dtml/imageEdit', globals(),
......@@ -803,6 +839,8 @@ class Image(File):
if content_type is not None:
self.content_type = content_type
self.ZCacheable_invalidate()
self.ZCacheable_set(None)
self.http__refreshEtag()
def __str__(self):
......
......@@ -4,4 +4,7 @@
<five:deprecatedManageAddDelete
class="OFS.userfolder.BasicUserFolder"/>
<five:deprecatedManageAddDelete
class="OFS.Cache.CacheManager"/>
</configure>
import unittest
from OFS.Cache import CacheManager
from OFS.Folder import Folder
from OFS.SimpleItem import SimpleItem
from OFS.metaconfigure import setDeprecatedManageAddDelete
class DummyCacheManager(CacheManager, SimpleItem):
def __init__(self, id, *args, **kw):
self.id = id
setDeprecatedManageAddDelete(DummyCacheManager)
class CacheTests(unittest.TestCase):
def test_managersExist(self):
from OFS.Cache import managersExist
from OFS.DTMLMethod import DTMLMethod
root = Folder('root')
root._setObject('root_cache', DummyCacheManager('root_cache'))
root._setObject('child', Folder('child'))
root.child._setObject('child_cache', DummyCacheManager('child_cache'))
root.child._setObject('child_content', DTMLMethod('child_content'))
# To begin with, cache managers will be found correctly
# using managersExist
self.assertTrue(managersExist(root.child.child_content))
# Now we delete the cache in the child folder
root.child.manage_delObjects(['child_cache'])
# The parent_cache should still trigger managersExist
self.assertTrue(managersExist(root.child.child_content))
......@@ -10,6 +10,8 @@ from io import BytesIO
from Acquisition import aq_base
from OFS.Application import Application
from OFS.SimpleItem import SimpleItem
from OFS.Cache import ZCM_MANAGERS
from OFS.Image import Pdata
from ZPublisher.HTTPRequest import HTTPRequest
from ZPublisher.HTTPResponse import HTTPResponse
......@@ -50,6 +52,41 @@ def aputrequest(file, content_type):
return req
class DummyCache(object):
def __init__(self):
self.clear()
def ZCache_set(self, ob, data, view_name='', keywords=None,
mtime_func=None):
self.set = (ob, data)
def ZCache_get(self, ob, data, view_name='', keywords=None,
mtime_func=None):
self.get = ob
if self.si:
return self.si
def ZCache_invalidate(self, ob):
self.invalidated = ob
def clear(self):
self.set = None
self.get = None
self.invalidated = None
self.si = None
def setStreamIterator(self, si):
self.si = si
ADummyCache = DummyCache()
class DummyCacheManager(SimpleItem):
def ZCacheManager_getCache(self):
return ADummyCache
class EventCatcher(object):
def __init__(self):
......@@ -97,9 +134,13 @@ class FileTests(unittest.TestCase):
self.root = a
responseOut = self.responseOut = BytesIO()
self.app = makerequest(self.root, stdout=responseOut)
self.app.dcm = DummyCacheManager()
factory = getattr(self.app, self.factory)
factory('file',
file=self.data, content_type=self.content_type)
self.app.file.ZCacheable_setManagerId('dcm')
self.app.file.ZCacheable_setEnabled(enabled=1)
setattr(self.app, ZCM_MANAGERS, ('dcm',))
# Hack, we need a _p_mtime for the file, so we make sure that it
# has one.
transaction.commit()
......@@ -128,6 +169,7 @@ class FileTests(unittest.TestCase):
del self.responseOut
del self.root
del self.connection
ADummyCache.clear()
self.eventCatcher.tearDown()
def testViewImageOrFile(self):
......@@ -137,6 +179,8 @@ class FileTests(unittest.TestCase):
self.file.update_data('foo')
self.assertEqual(self.file.size, 3)
self.assertEqual(self.file.data, 'foo')
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
def testReadData(self):
s = "a" * (2 << 16)
......@@ -161,6 +205,8 @@ class FileTests(unittest.TestCase):
self.file.manage_edit('foobar', 'text/plain', filedata='ASD')
self.assertEqual(self.file.title, 'foobar')
self.assertEqual(self.file.content_type, 'text/plain')
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
self.assertEqual(1, len(self.eventCatcher.modified))
self.assertTrue(self.eventCatcher.modified[0].object is self.file)
......@@ -168,6 +214,7 @@ class FileTests(unittest.TestCase):
self.file.manage_edit('foobar', 'text/plain')
self.assertEqual(self.file.title, 'foobar')
self.assertEqual(self.file.content_type, 'text/plain')
self.assertTrue(ADummyCache.invalidated)
self.assertEqual(1, len(self.eventCatcher.modified))
self.assertTrue(self.eventCatcher.modified[0].object is self.file)
......@@ -256,6 +303,8 @@ class ImageTests(FileTests):
self.assertEqual(self.file.data, self.data)
self.assertEqual(self.file.width, 16)
self.assertEqual(self.file.height, 16)
self.assertTrue(ADummyCache.invalidated)
self.assertTrue(ADummyCache.set)
def testStr(self):
self.assertEqual(
......
......@@ -88,7 +88,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
manage_options = (
{'label': 'Edit', 'action': 'pt_editForm'},
) + SimpleItem.manage_options
) + SimpleItem.manage_options + Cacheable.manage_options
_properties = (
{'id': 'title', 'type': 'ustring', 'mode': 'w'},
......@@ -164,6 +164,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
if not is_unicode:
text = unicode(text, encoding)
self.ZCacheable_invalidate()
super(ZopePageTemplate, self).pt_edit(text, content_type)
pt_editForm = PageTemplateFile('www/ptEdit', globals(),
......@@ -203,6 +204,11 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
title = unicode(title, encoding)
self._setPropValue('title', title)
def _setPropValue(self, id, value):
""" set a property and invalidate the cache """
PropertyManager._setPropValue(self, id, value)
self.ZCacheable_invalidate()
security.declareProtected(change_page_templates, 'pt_upload')
def pt_upload(self, REQUEST, file='', encoding='utf-8'):
"""Replace the document with the text in file."""
......@@ -248,6 +254,7 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
preferred_encodings)
self.output_encoding = encoding
self.ZCacheable_invalidate()
ZopePageTemplate.inheritedAttribute('write')(self, text)
def _exec(self, bound_names, args, kw):
......@@ -265,11 +272,25 @@ class ZopePageTemplate(Script, PageTemplate, Cacheable,
security = getSecurityManager()
bound_names['user'] = security.getUser()
# Retrieve the value from the cache.
keyset = None
if self.ZCacheable_isCachingEnabled():
# Prepare a cache key.
keyset = {'here': self._getContext(),
'bound_names': bound_names}
result = self.ZCacheable_get(keywords=keyset)
if result is not None:
# Got a cached value.
return result
# Execute the template in a new security context.
security.addContext(self)
try:
result = self.pt_render(extra_context=bound_names)
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
return result
finally:
security.removeContext(self)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment