
On Tue, 2005-02-01 at 16:05 +0000, Valentino Volonghi aka Dialtone wrote:
On Tue, 01 Feb 2005 12:58:49 GMT, Valentino Volonghi aka Dialtone <dialtone@divmod.com> wrote:
The first version of the patch didn't actually work. But I wrote a new version, also thanks to fzZzy and this time it works although it has a flaw since in weever caching the content slot (which is filled with a Fragment) results in 2 big red Nones and the rendered fragment.
As I said in the first mail you can use caching with:
t.cached(name=some_sensible_name, lifetime=MAX_LIFE)[cached_content]
This patch provides, probably, the finest granularity in caching the rendering.
Anyway the patch is below:
I think it would be much better if the _CACHE module-scope dict was replaced with an object remembered in the context. There are a couple of reasons for this: * We can have persistence to the file system when necessary. * We can remember a cache manager on a resource to allow drop-in components (and their child resources) to manage their own caching. This also allows some root resource class to have multiple instances, where the interface names used as the cache keys will likely be the same, to be deployed under a single site. * The cache manager API can be extended in the future to allow manual clearing of cache items, i.e. some public web UI can cache parts of the page indefintely and an admin UI (that shares the same cache manager) can clear cached data as objects are modified. There are also a couple of features that I can see stan.cached "growing" later on. I've mentioned some of these on IRC. * Cache scope, i.e. application vs session. As I've said on IRC, I can see a real use case for session-scoped caching, i.e. I get my cached version; you get yours. * It might be nice to allow timeouts to be defined as "every hour", "every fifteen minutes", "at 12am". Yeah, I'm talking cron-like ;-). Hmm, one last idea is cache groups. I think this is especially applicable to the above idea of having an API to clear cache objects. Say some part of the page includes content from two objects: a Foo with id 3 and a Bar with id 8. The fragment could be cached against the key ((Foo,3),(Bar,8)). If some user then changed the Foo,3 object it would clear (Foo,3) cached objects; if Bar,8 was changed it would clear (Bar,8) cached objects. Either one would remove the ((Foo,3),(Bar,8)) cached content. Obviously, it would be up to the application to choose its keys carefull but, basically, if the equivalent of "(Foo,3) in ((Foo,3),(Bar,8))" succeeds then the object would be cleared. I don't think we have to add all these features right now as long as the initial API takes these sorts of use cases into consideration. Cheers, Matt
Index: nevow/tags.py =================================================================== --- nevow/tags.py (revision 1136) +++ nevow/tags.py (working copy) @@ -25,7 +25,7 @@ """
-from nevow.stan import Proto, Tag, directive, raw, xml, CommentProto, invisible, slot, cdata +from nevow.stan import Proto, Tag, directive, raw, xml, CommentProto, invisible, slot, cdata, cached
comment = CommentProto() @@ -62,7 +62,9 @@ def inlineJS(s): return script(type="text/javascript", language="JavaScript")[xml('\n//<![CDATA[\n%s\n//]]>\n' % s)]
-__all__ = tags + ['invisible', 'comment', '_dir', '_del', '_object', '_map', 'drange', 'Tag', 'directive', 'xml', 'raw', 'slot', 'cdata', 'inlineJS'] + ['_%s' % x for x in range(100)] +__all__ = tags + ['invisible', 'comment', '_dir', '_del', '_object', + '_map', 'drange', 'Tag', 'directive', 'xml', 'raw', + 'slot', 'cached', 'cdata', 'inlineJS'] + ['_%s' % x for x in range(100)]
######################## Index: nevow/flat/flatstan.py =================================================================== --- nevow/flat/flatstan.py (revision 1136) +++ nevow/flat/flatstan.py (working copy) @@ -9,7 +9,7 @@ from nevow import util from nevow.stan import Proto, Tag, xml, directive, Unset, invisible from nevow.inevow import IRenderer, IRendererFactory, IGettable, IData -from nevow.flat import precompile, serialize +from nevow.flat import precompile, serialize, iterflatten from nevow.accessors import convertToData from nevow.context import WovenContext
@@ -226,6 +226,56 @@ return serialize(original.default, context) return serialize(data, context)
+_CACHE = {} +from time import time as now +from cStringIO import StringIO +from twisted.internet import defer +def CachedSerializer(original, context): + cached = _CACHE.get(original.name, None) + life = now()-original.lifetime + if cached and cached[0] > life: +## print "="*20 +## print cached[0] +## print life +## print "="*20 + yield cached[1] + return +## if cached: +## print "="*20 +## print cached[0] +## print life +## print "="*20 + io = StringIO() + for child in iterflatten(original.children, context, io.write, + lambda item: True): + if isinstance(child, tuple): + childDeferred, childReturner = child + + d = defer.Deferred() ## A new deferred for the outer loop, whose result + ## we don't care about, because we don't want the outer loop to write + ## anything when this deferred fires -- only when the entire for loop + ## has completed and we have all the "children" flattened + + def innerDeferredResultAvailable(result): + childReturner(result) ## Cause the inner iterflatten to continue + d.callback('') ## Cause the outer iterflatten to continue + return '' + + childDeferred.addCallback(innerDeferredResultAvailable) + + ## Make the outer loop wait on our new deferred. + ## We call the new deferred back with '' + ## Which will cause the outer loop to write '' to the request, + ## which doesn't matter. It will then call our "returner", + ## which is just the noop lambda below, because we don't care + ## about the return result of the new deferred, which is just + ## '' + + yield d, lambda result: None + result = io.getvalue() + _CACHE[original.name] = (now(), result) + yield result + def ContextSerializer(original, context): originalContext = original.clone(deep=False) originalContext.precompile = context and context.precompile or False Index: nevow/__init__.py =================================================================== --- nevow/__init__.py (revision 1136) +++ nevow/__init__.py (working copy) @@ -182,6 +182,7 @@ nevow.flat.flatstan.RendererSerializer nevow.inevow.IRenderer nevow.flat.flatstan.DirectiveSerializer nevow.stan.directive nevow.flat.flatstan.SlotSerializer nevow.stan.slot +nevow.flat.flatstan.CachedSerializer nevow.stan.cached nevow.flat.flatstan.ContextSerializer nevow.context.WovenContext nevow.flat.flatstan.DeferredSerializer twisted.internet.defer.Deferred nevow.flat.flatstan.DeferredSerializer twisted.internet.defer.DeferredList Index: nevow/stan.py =================================================================== --- nevow/stan.py (revision 1136) +++ nevow/stan.py (working copy) @@ -119,8 +119,33 @@ """ raise NotImplementedError, "Stan slot instances are not iterable."
+class cached(object): + """Marker for cached content + """ + __slots__ = ['name', 'children', 'lifetime']
+ def __init__(self, name, lifetime=0): + self.name = name + self.children = [] + self.lifetime = lifetime
+ def __repr__(self): + return "cached('%s','%s')" % self.name, self.lifetime + + def __getitem__(self, children): + """cached content is what is being cached + """ + if not isinstance(children, (list, tuple)): + children = [children] + self.children.extend(children) + return self + + def __iter__(self): + """Prevent an infinite loop if someone tries to do + for x in cached('foo'): + """ + raise NotImplementedError, "Stan slot instances are not iterable." + class Tag(object): """Tag instances represent XML tags with a tag name, attributes, and children. Tag instances can be constructed using the Prototype
_______________________________________________ Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web