Fix for an issue 92 (windows binary distribution made with distutils doesn't install data files into package directories)

Hello!
While trying to fix issue 92 [1] I came to PEAK package. To solve the problems with distutils they use package 'setuptools' from python cvs repository. They just add 'setuptools' to PEAK repository and use it in setup.py [2].
So I tried to do the same for nevow. My results (and longer description) are attached to issue 92 [1]. For example, with 'setuptools' nevow's setup.py become:
===[...setup.py...]=== #!/usr/bin/env python2.3 # -*- test-case-name: "nevow.test -xformless.test" -*-
from setuptools import setup, find_packages import sys
packages = find_packages()
## Prevent 'setuptools' package to be included in certain cases cmd = sys.argv[1] if cmd.startswith('bdist') or cmd.startswith('install'): packages = [pkg for pkg in packages if not pkg.startswith('setuptools')]
setup( author='Donovan Preston et al', author_email='dp@divmod.org', name='nevow', version='0.4pre', description='Web Application Construction Kit', url='http://www.nevow.com/', packages = packages, package_data = { 'formless': ['freeform-default.css'], 'nevow': ['liveevil.js','Canvas.swf'], }, scripts=['bin/nevow-xmlgettext'], ) ===[...setup.py...]===
P.S. What about documentation issues 158 [3] and 159 [4]? For some reason I haven't recieved *any* response. Is it because nobody is interested in them?
Thank you!
[1] http://divmod.org/users/roundup.twistd/nevow/issue92 [2] http://cvs.eby-sarna.com/PEAK/setup.py?rev=1.101&content-type=text/vnd.v... [3] http://divmod.org/users/roundup.twistd/nevow/issue158 [4] http://divmod.org/users/roundup.twistd/nevow/issue159

Hello!
There is also an issue with MANIFEST.in:
http://divmod.org/users/roundup.twistd/nevow/issue167

On Mar 2, 2005, at 6:28 AM, Alexey Shamrin wrote:
P.S. What about documentation issues 158 [3] and 159 [4]? For some reason I haven't recieved *any* response. Is it because nobody is interested in them?
It is not that nobody is interested, but that everyone is very busy. I apologize.
dp

Hello Donovan!
You don't need to apologize. Your answer is the response I was waiting for %-)
Can I expect that someday someone will look at my patches?
Best regards, Alexey.
On Wed, 2 Mar 2005 07:22:56 -0800, Donovan Preston dp@ulaluma.com wrote:
On Mar 2, 2005, at 6:28 AM, Alexey Shamrin wrote:
P.S. What about documentation issues 158 [3] and 159 [4]? For some reason I haven't recieved *any* response. Is it because nobody is interested in them?
It is not that nobody is interested, but that everyone is very busy. I apologize.
dp
Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web

On Wed, 2005-03-02 at 22:56 +0300, Alexey Shamrin wrote:
Hello Donovan!
You don't need to apologize. Your answer is the response I was waiting for %-)
Can I expect that someday someone will look at my patches?
Yes, someone will.
I actually started a reply to one of your previous emails (22 Feb) but got sidetracked by work and it looks like I never even sent it in the end. Sorry about that.
Please, please, please keep submitting fixes. It you submit enough you'll probably just get commit access anyway so we don't get fed up reviewing stuff :wink:.
- Matt
Best regards, Alexey.
On Wed, 2 Mar 2005 07:22:56 -0800, Donovan Preston dp@ulaluma.com wrote:
On Mar 2, 2005, at 6:28 AM, Alexey Shamrin wrote:
P.S. What about documentation issues 158 [3] and 159 [4]? For some reason I haven't recieved *any* response. Is it because nobody is interested in them?
It is not that nobody is interested, but that everyone is very busy. I apologize.
dp
Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web
Twisted-web mailing list Twisted-web@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web

On Wed, Mar 02, 2005 at 10:48:03PM +0000, Matt Goodall wrote:
Yes, someone will.
btw, I would also like to send a reminder about getting the caching stuff into the trunk ;). I proved that the hard approach in rend.Page is significantly more performant than the tags.cached pure approach (even the hard approach is less clean).
This is what I carry in my own Nevow branch (most of it is dialtone's Nevow-caching branch):
Index: Nevow/nevow/tags.py =================================================================== --- Nevow/nevow/tags.py (revision 1257) +++ Nevow/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/nevow/__init__.py =================================================================== --- Nevow/nevow/__init__.py (revision 1257) +++ Nevow/nevow/__init__.py (working copy) @@ -138,6 +138,8 @@ nevow.util.remainingSegmentsFactory nevow.context.RequestContext nevow.inevow.IRemainingSegments nevow.util.currentSegmentsFactory nevow.context.RequestContext nevow.inevow.ICurrentSegments
+nevow.cache.SiteCache nevow.context.SiteContext nevow.inevow.ICache + nevow.query.QueryContext nevow.context.WovenContext nevow.inevow.IQ nevow.query.QueryLoader nevow.inevow.IDocFactory nevow.inevow.IQ nevow.query.QueryList __builtin__.list nevow.inevow.IQ @@ -186,6 +188,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/nevow/flat/flatstan.py =================================================================== --- Nevow/nevow/flat/flatstan.py (revision 1257) +++ Nevow/nevow/flat/flatstan.py (working copy) @@ -8,11 +8,15 @@
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.inevow import IRenderer, IRendererFactory, IGettable, IData, ICache +from nevow.flat import precompile, serialize, iterflatten from nevow.accessors import convertToData from nevow.context import WovenContext
+from time import time as now +from cStringIO import StringIO +from twisted.internet import defer + allowSingleton = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area', 'input', 'col', 'basefont', 'isindex', 'frame')
@@ -226,6 +230,43 @@ return serialize(original.default, context) return serialize(data, context)
+def CachedSerializer(original, context): + cache = ICache(original.scope(context)) + cached = cache.get(original.key, original.lifetime) + if cached: + yield cached + return + 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: '' + result = io.getvalue() + cache.set(result, original.key) + yield result + def ContextSerializer(original, context): originalContext = original.clone(deep=False) originalContext.precompile = context and context.precompile or False Index: Nevow/nevow/stan.py =================================================================== --- Nevow/nevow/stan.py (revision 1257) +++ Nevow/nevow/stan.py (working copy) @@ -119,8 +119,40 @@ """ raise NotImplementedError, "Stan slot instances are not iterable."
+def passThrough(_): + return _
+class cached(object): + """Marker for cached content + """ + __slots__ = ['key', 'children', 'lifetime', 'scope']
+ def __init__(self, key, scope=None, lifetime=-1): + self.key = key + self.children = [] + self.lifetime = lifetime + self.scope = scope + if not scope: + self.scope = passThrough + + + def __repr__(self): + return "cached('%s','%s')" % self.key, 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 Index: Nevow/nevow/inevow.py =================================================================== --- Nevow/nevow/inevow.py (revision 1257) +++ Nevow/nevow/inevow.py (working copy) @@ -98,8 +98,24 @@
ANY python object is said to implement IData. """ +class ICache(compy.Interface): + """This object represents the cache that contains all the + pre-flattened fragments + """ + def get(self, index, lifetime): + """ Get an object from the cache with the given index only if + it is less old than lifetime, otherwise return None. + """
+ def set(self, toBeCached, *indexes): + """ Register toBeCached with each of the indexes passed """
+ def clear(self, what): + """ Clear what keyed element from the cache, or search for + what in sequences in all the keys and clear the item + """ + + class IGettable(compy.Interface): def get(self, context): """Return the data Index: Nevow/nevow/rend.py =================================================================== --- Nevow/nevow/rend.py (revision 1257) +++ Nevow/nevow/rend.py (working copy) @@ -30,6 +30,7 @@ from nevow import flat from nevow.util import log from nevow import util +from nevow import url
import formless from formless import iformless @@ -376,6 +377,8 @@ self.children[name] = child
+_CACHE = {} + class Page(Fragment, ConfigurableFactory, ChildLookupMixin): """A page is the main Nevow resource and renders a document loaded via the document factory (docFactory). @@ -389,8 +392,37 @@ afterRender = None addSlash = None
+ cache = False + lifetime = -1 + __lastCacheRendering = 0 + flattenFactory = flat.flattenFactory
+ def hasCache(self, ctx): + if not self.cache: + return + + _now = now() # run gettimeofday only once + timeout = _now > self.__lastCacheRendering + self.lifetime and \ + self.lifetime >= 0 + c = self.lookupCache(ctx) + if timeout or c is None: + # stop other renders + self.__lastCacheRendering = _now + c = None + return c + def cacheRendered(self, ctx, c): + if not self.cache: + return + # overwrite the deferred with the data + self.storeCache(ctx, c) + def cacheIDX(self, ctx): + return str(url.URL.fromContext(ctx)) + def storeCache(self, ctx, c): + _CACHE[self.cacheIDX(ctx)] = c + def lookupCache(self, ctx): + return _CACHE.get(self.cacheIDX(ctx)) + def renderHTTP(self, ctx): ## XXX request is really ctx now, change the name here request = inevow.IRequest(ctx) @@ -412,11 +444,18 @@ if self.afterRender is not None: self.afterRender(ctx)
- if self.buffered: + c = self.hasCache(ctx) + if c is not None: + finishRequest() + return c + + if self.buffered or self.cache: io = StringIO() writer = io.write def finisher(result): - request.write(io.getvalue()) + c = io.getvalue() + self.cacheRendered(ctx, c) + request.write(c) finishRequest() return result else: @@ -500,7 +539,6 @@ else: ## Use the redirectAfterPost url ref = str(redirectAfterPost) - from nevow import url refpath = url.URL.fromString(ref) magicCookie = str(now()) refpath = refpath.replace('_nevow_carryover_', magicCookie) Index: Nevow/nevow/guard.py =================================================================== --- Nevow/nevow/guard.py (revision 1257) +++ Nevow/nevow/guard.py (working copy) @@ -24,7 +24,7 @@ from twisted.protocols import http
# Nevow imports -from nevow import inevow, url, stan +from nevow import inevow, url, stan, cache
def _sessionCookie(): @@ -315,6 +315,7 @@ path="/%s" % '/'.join(request.prepath), secure=secure) sz = self.sessions[newCookie] = self.sessionFactory(self, newCookie) + sz.setComponent(inevow.ICache, cache.SessionCache()) sz.args = request.args sz.fields = getattr(request, 'fields', {}) sz.content = request.content Index: Nevow/nevow/cache.py =================================================================== --- Nevow/nevow/cache.py (revision 0) +++ Nevow/nevow/cache.py (revision 0) @@ -0,0 +1,33 @@ +from time import time as now +from nevow import inevow + +class SiteCache(object): + __implements__ = inevow.ICache, + _content = {} + def __init__(self, original): + self.original = original + + def get(self, index, lifetime): + cached = self._content.get(index, None) + if cached is None: + return + if lifetime < 0: + return cached[1] + if cached[0] + lifetime > now(): + return cached[1] + + def set(self, toBeCached, *indexes): + _now = now() + for index in indexes: + self._content[index] = (_now, toBeCached) + + def clear(self, what): + if self._content.has_key(what): + self._content.pop(what) + for key in self._content.keys(): + if what in key: + self._content.pop(key) + +class SessionCache(SiteCache): + def __init__(self): + self._content = {}
This code is working for a few weeks on www.cpushare.com, so far so good (all http part is completely cached with the rand.Page lifetime and it delivers >200 req per second of those small pages). I also use the tags.caching from dialtime in various places.
Thanks a lot to dialtone and everyone else for making this possible.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
+1 performance-enhancing drugs.
BTW, is there an example of using cached anywhere?
C
Andrea Arcangeli wrote: | On Wed, Mar 02, 2005 at 10:48:03PM +0000, Matt Goodall wrote: | |>Yes, someone will. | | | btw, I would also like to send a reminder about getting the caching | stuff into the trunk ;). I proved that the hard approach in rend.Page is | significantly more performant than the tags.cached pure approach (even | the hard approach is less clean). | | This is what I carry in my own Nevow branch (most of it is dialtone's | Nevow-caching branch): | | Index: Nevow/nevow/tags.py | =================================================================== | --- Nevow/nevow/tags.py (revision 1257) | +++ Nevow/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/nevow/__init__.py | =================================================================== | --- Nevow/nevow/__init__.py (revision 1257) | +++ Nevow/nevow/__init__.py (working copy) | @@ -138,6 +138,8 @@ | nevow.util.remainingSegmentsFactory nevow.context.RequestContext nevow.inevow.IRemainingSegments | nevow.util.currentSegmentsFactory nevow.context.RequestContext nevow.inevow.ICurrentSegments | | +nevow.cache.SiteCache nevow.context.SiteContext nevow.inevow.ICache | + | nevow.query.QueryContext nevow.context.WovenContext nevow.inevow.IQ | nevow.query.QueryLoader nevow.inevow.IDocFactory nevow.inevow.IQ | nevow.query.QueryList __builtin__.list nevow.inevow.IQ | @@ -186,6 +188,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/nevow/flat/flatstan.py | =================================================================== | --- Nevow/nevow/flat/flatstan.py (revision 1257) | +++ Nevow/nevow/flat/flatstan.py (working copy) | @@ -8,11 +8,15 @@ | | 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.inevow import IRenderer, IRendererFactory, IGettable, IData, ICache | +from nevow.flat import precompile, serialize, iterflatten | from nevow.accessors import convertToData | from nevow.context import WovenContext | | +from time import time as now | +from cStringIO import StringIO | +from twisted.internet import defer | + | allowSingleton = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area', | 'input', 'col', 'basefont', 'isindex', 'frame') | | @@ -226,6 +230,43 @@ | return serialize(original.default, context) | return serialize(data, context) | | +def CachedSerializer(original, context): | + cache = ICache(original.scope(context)) | + cached = cache.get(original.key, original.lifetime) | + if cached: | + yield cached | + return | + 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: '' | + result = io.getvalue() | + cache.set(result, original.key) | + yield result | + | def ContextSerializer(original, context): | originalContext = original.clone(deep=False) | originalContext.precompile = context and context.precompile or False | Index: Nevow/nevow/stan.py | =================================================================== | --- Nevow/nevow/stan.py (revision 1257) | +++ Nevow/nevow/stan.py (working copy) | @@ -119,8 +119,40 @@ | """ | raise NotImplementedError, "Stan slot instances are not iterable." | | +def passThrough(_): | + return _ | | +class cached(object): | + """Marker for cached content | + """ | + __slots__ = ['key', 'children', 'lifetime', 'scope'] | | + def __init__(self, key, scope=None, lifetime=-1): | + self.key = key | + self.children = [] | + self.lifetime = lifetime | + self.scope = scope | + if not scope: | + self.scope = passThrough | + | + | + def __repr__(self): | + return "cached('%s','%s')" % self.key, 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 | Index: Nevow/nevow/inevow.py | =================================================================== | --- Nevow/nevow/inevow.py (revision 1257) | +++ Nevow/nevow/inevow.py (working copy) | @@ -98,8 +98,24 @@ | | ANY python object is said to implement IData. | """ | +class ICache(compy.Interface): | + """This object represents the cache that contains all the | + pre-flattened fragments | + """ | + def get(self, index, lifetime): | + """ Get an object from the cache with the given index only if | + it is less old than lifetime, otherwise return None. | + """ | | + def set(self, toBeCached, *indexes): | + """ Register toBeCached with each of the indexes passed """ | | + def clear(self, what): | + """ Clear what keyed element from the cache, or search for | + what in sequences in all the keys and clear the item | + """ | + | + | class IGettable(compy.Interface): | def get(self, context): | """Return the data | Index: Nevow/nevow/rend.py | =================================================================== | --- Nevow/nevow/rend.py (revision 1257) | +++ Nevow/nevow/rend.py (working copy) | @@ -30,6 +30,7 @@ | from nevow import flat | from nevow.util import log | from nevow import util | +from nevow import url | | import formless | from formless import iformless | @@ -376,6 +377,8 @@ | self.children[name] = child | | | +_CACHE = {} | + | class Page(Fragment, ConfigurableFactory, ChildLookupMixin): | """A page is the main Nevow resource and renders a document loaded | via the document factory (docFactory). | @@ -389,8 +392,37 @@ | afterRender = None | addSlash = None | | + cache = False | + lifetime = -1 | + __lastCacheRendering = 0 | + | flattenFactory = flat.flattenFactory | | + def hasCache(self, ctx): | + if not self.cache: | + return | + | + _now = now() # run gettimeofday only once | + timeout = _now > self.__lastCacheRendering + self.lifetime and \ | + self.lifetime >= 0 | + c = self.lookupCache(ctx) | + if timeout or c is None: | + # stop other renders | + self.__lastCacheRendering = _now | + c = None | + return c | + def cacheRendered(self, ctx, c): | + if not self.cache: | + return | + # overwrite the deferred with the data | + self.storeCache(ctx, c) | + def cacheIDX(self, ctx): | + return str(url.URL.fromContext(ctx)) | + def storeCache(self, ctx, c): | + _CACHE[self.cacheIDX(ctx)] = c | + def lookupCache(self, ctx): | + return _CACHE.get(self.cacheIDX(ctx)) | + | def renderHTTP(self, ctx): | ## XXX request is really ctx now, change the name here | request = inevow.IRequest(ctx) | @@ -412,11 +444,18 @@ | if self.afterRender is not None: | self.afterRender(ctx) | | - if self.buffered: | + c = self.hasCache(ctx) | + if c is not None: | + finishRequest() | + return c | + | + if self.buffered or self.cache: | io = StringIO() | writer = io.write | def finisher(result): | - request.write(io.getvalue()) | + c = io.getvalue() | + self.cacheRendered(ctx, c) | + request.write(c) | finishRequest() | return result | else: | @@ -500,7 +539,6 @@ | else: | ## Use the redirectAfterPost url | ref = str(redirectAfterPost) | - from nevow import url | refpath = url.URL.fromString(ref) | magicCookie = str(now()) | refpath = refpath.replace('_nevow_carryover_', magicCookie) | Index: Nevow/nevow/guard.py | =================================================================== | --- Nevow/nevow/guard.py (revision 1257) | +++ Nevow/nevow/guard.py (working copy) | @@ -24,7 +24,7 @@ | from twisted.protocols import http | | # Nevow imports | -from nevow import inevow, url, stan | +from nevow import inevow, url, stan, cache | | | def _sessionCookie(): | @@ -315,6 +315,7 @@ | path="/%s" % '/'.join(request.prepath), | secure=secure) | sz = self.sessions[newCookie] = self.sessionFactory(self, newCookie) | + sz.setComponent(inevow.ICache, cache.SessionCache()) | sz.args = request.args | sz.fields = getattr(request, 'fields', {}) | sz.content = request.content | Index: Nevow/nevow/cache.py | =================================================================== | --- Nevow/nevow/cache.py (revision 0) | +++ Nevow/nevow/cache.py (revision 0) | @@ -0,0 +1,33 @@ | +from time import time as now | +from nevow import inevow | + | +class SiteCache(object): | + __implements__ = inevow.ICache, | + _content = {} | + def __init__(self, original): | + self.original = original | + | + def get(self, index, lifetime): | + cached = self._content.get(index, None) | + if cached is None: | + return | + if lifetime < 0: | + return cached[1] | + if cached[0] + lifetime > now(): | + return cached[1] | + | + def set(self, toBeCached, *indexes): | + _now = now() | + for index in indexes: | + self._content[index] = (_now, toBeCached) | + | + def clear(self, what): | + if self._content.has_key(what): | + self._content.pop(what) | + for key in self._content.keys(): | + if what in key: | + self._content.pop(key) | + | +class SessionCache(SiteCache): | + def __init__(self): | + self._content = {} | | This code is working for a few weeks on www.cpushare.com, so far so | good (all http part is completely cached with the rand.Page lifetime and | it delivers >200 req per second of those small pages). I also use the | tags.caching from dialtime in various places. | | Thanks a lot to dialtone and everyone else for making this possible. | | _______________________________________________ | Twisted-web mailing list | Twisted-web@twistedmatrix.com | http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-web

On Thu, Mar 03, 2005 at 09:33:42AM -0800, Cory Dodt wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
+1 performance-enhancing drugs.
BTW, is there an example of using cached anywhere?
weever has some example for tags cached.
The hard rand.Page cache is much simpler to use, my code looks like this:
LIFETIME = 60
class forever_cached_page_class(rend.Page): cache = True class cached_page_class(forever_cached_page_class): lifetime = LIFETIME
class myclass(rend.Page, cached_page_class): ...
class myclass(rand.Page, forever_cached_page_class): ...
If the renderings are static you can cache forever, mine are dynamic over time, so I more frequently use multiple inheritance with cached_page_class.
Trivial isn't it?
It has taken me _minutes_ to cache my whole site.
Try w/ cache and w/o cache ab2 -n 1000 -c 100 localhost:8080/
Still there are parts where I don't cache anything (since I don't want to complicate things too much), and for those the optimizations in compy should payoff significantly.
Thanks!

Andrea Arcangeli wrote:
On Wed, Mar 02, 2005 at 10:48:03PM +0000, Matt Goodall wrote:
Yes, someone will.
btw, I would also like to send a reminder about getting the caching stuff into the trunk ;). I proved that the hard approach in rend.Page is significantly more performant than the tags.cached pure approach (even the hard approach is less clean).
This is what I carry in my own Nevow branch (most of it is dialtone's Nevow-caching branch):
The patch has been applied (with some improvements) in the caching branch.
I'm on your side here and I would like to make a decision on this feature.
We have 2 implementations: foom's: The cache is keyed with the interfaces' values that the cached tag uses internally and a new context, that only remembers those selected interfaces, is created and passed through the cached tag to render its content. + very secure - hard to understand why you should always key with at least IRendererFactory
mine: The cache can be keyed with any object, if you pass a sequence the cached tag will be registered for each item in the sequence and the sequence itself. An optional scope argument can be used to tell where the cached content should be stored (anything that implements ICache can act as a CacheStore), by default there are 2 scopes: sitewide (without any extra argument passed to t.cached tag) and ISession (which stores a cached item per user). + easier to understand for a beginner - less secure and harder to use, since you have to manually provide a unique key, when there are many many many cached items.
Now, I must say that I like foom's idea but it need a bit of tweaking IMHO to allow easier use for beginners.

On Thu, Mar 03, 2005 at 11:51:25PM +0100, Valentino Volonghi wrote:
The patch has been applied (with some improvements) in the caching branch.
Thanks! I'll check it.
I'm on your side here and I would like to make a decision on this feature.
Sure.
The cache is keyed with the interfaces' values that the cached tag uses internally and a new context, that only remembers those selected interfaces, is created and passed through the cached tag to render its content.
- very secure
- hard to understand why you should always key with at least
IRendererFactory
Interfaces are nice in theory, but I'm not a big fan of interfaces in practice. I find often just a slowdown without no advantages (yeah, class namespace __dict__ remains clean, but often there is no other advantage), so I doubt I'll like the IRendererFactory way. In my own twisted code (not the web part) I intentionally never use interfaces to be sure not to run into unnecessary slowdowns.
How would I implement the equivalent of this?
def render_copyright(self, ctx, data): return ctx.tag[tags.cached('copyright_class-' + str(self.inside_account(ctx)), lifetime = LIFETIME)[ copyright_class()]]
self.inside_account(ctx) returns True or False, I'm just trying to understand the details of the interface way.
participants (6)
-
Alexey Shamrin
-
Andrea Arcangeli
-
Cory Dodt
-
Donovan Preston
-
Matt Goodall
-
Valentino Volonghi aka Dialtone