Permalink
Browse files

Add Cheetah package

  • Loading branch information...
bcui6611 committed Jun 12, 2012
1 parent a1f7ce3 commit 8f32f042632ed3f7bf8a857f10a2d465c49b5402
Showing with 15,887 additions and 0 deletions.
  1. +136 −0 Cheetah/CacheRegion.py
  2. +106 −0 Cheetah/CacheStore.py
  3. +98 −0 Cheetah/DirectiveAnalyzer.py
  4. +16 −0 Cheetah/Django.py
  5. +108 −0 Cheetah/DummyTransaction.py
  6. +62 −0 Cheetah/ErrorCatchers.py
  7. +357 −0 Cheetah/FileUtils.py
  8. +212 −0 Cheetah/Filters.py
  9. +129 −0 Cheetah/ImportHooks.py
  10. +541 −0 Cheetah/ImportManager.py
  11. +67 −0 Cheetah/Macros/I18n.py
  12. +1 −0 Cheetah/Macros/__init__.py
  13. +2,661 −0 Cheetah/Parser.py
  14. +48 −0 Cheetah/Servlet.py
  15. +284 −0 Cheetah/SettingsManager.py
  16. +267 −0 Cheetah/SourceReader.py
  17. +1,941 −0 Cheetah/Template.py
  18. +107 −0 Cheetah/TemplateCmdLineIface.py
  19. +272 −0 Cheetah/Templates/SkeletonPage.py
  20. +44 −0 Cheetah/Templates/SkeletonPage.tmpl
  21. +215 −0 Cheetah/Templates/_SkeletonPage.py
  22. +1 −0 Cheetah/Templates/__init__.py
  23. +29 −0 Cheetah/Tests/Analyzer.py
  24. +579 −0 Cheetah/Tests/CheetahWrapper.py
  25. +39 −0 Cheetah/Tests/Cheps.py
  26. +70 −0 Cheetah/Tests/Filters.py
  27. +20 −0 Cheetah/Tests/Misc.py
  28. +548 −0 Cheetah/Tests/NameMapper.py
  29. +49 −0 Cheetah/Tests/Parser.py
  30. +243 −0 Cheetah/Tests/Performance.py
  31. +247 −0 Cheetah/Tests/Regressions.py
  32. +3,253 −0 Cheetah/Tests/SyntaxAndOutput.py
  33. +363 −0 Cheetah/Tests/Template.py
  34. +53 −0 Cheetah/Tests/Test.py
  35. +237 −0 Cheetah/Tests/Unicode.py
  36. +1 −0 Cheetah/Tests/__init__.py
  37. +381 −0 Cheetah/Tests/xmlrunner.py
  38. +77 −0 Cheetah/Tools/CGITemplate.py
  39. +464 −0 Cheetah/Tools/MondoReport.py
  40. +391 −0 Cheetah/Tools/MondoReportDoc.txt
  41. +28 −0 Cheetah/Tools/RecursiveNull.py
  42. +166 −0 Cheetah/Tools/SiteHierarchy.py
  43. +8 −0 Cheetah/Tools/__init__.py
  44. +5 −0 Cheetah/Tools/turbocheetah/__init__.py
  45. +110 −0 Cheetah/Tools/turbocheetah/cheetahsupport.py
  46. +1 −0 Cheetah/Tools/turbocheetah/tests/__init__.py
  47. +66 −0 Cheetah/Tools/turbocheetah/tests/test_template.py
  48. +9 −0 Cheetah/Unspecified.py
  49. +123 −0 Cheetah/Utils/Indenter.py
  50. +67 −0 Cheetah/Utils/Misc.py
  51. +102 −0 Cheetah/Utils/WebInputMixin.py
  52. +1 −0 Cheetah/Utils/__init__.py
  53. +14 −0 Cheetah/Utils/htmlDecode.py
  54. +21 −0 Cheetah/Utils/htmlEncode.py
  55. +304 −0 Cheetah/Utils/statprof.py
  56. +58 −0 Cheetah/Version.py
  57. +20 −0 Cheetah/__init__.py
  58. +47 −0 Cheetah/c/Cheetah.h
  59. +20 −0 Cheetah/convertTmplPathToModuleName.py
View
@@ -0,0 +1,136 @@
+# $Id: CacheRegion.py,v 1.3 2006/01/28 04:19:30 tavis_rudd Exp $
+'''
+Cache holder classes for Cheetah:
+
+Cache regions are defined using the #cache Cheetah directive. Each
+cache region can be viewed as a dictionary (keyed by cacheRegionID)
+handling at least one cache item (the default one). It's possible to add
+cacheItems in a region by using the `varyBy` #cache directive parameter as
+in the following example::
+ #def getArticle
+ this is the article content.
+ #end def
+
+ #cache varyBy=$getArticleID()
+ $getArticle($getArticleID())
+ #end cache
+
+The code above will generate a CacheRegion and add new cacheItem for each value
+of $getArticleID().
+'''
+
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import md5
+
+import time
+import Cheetah.CacheStore
+
+class CacheItem(object):
+ '''
+ A CacheItem is a container storing:
+
+ - cacheID (string)
+ - refreshTime (timestamp or None) : last time the cache was refreshed
+ - data (string) : the content of the cache
+ '''
+
+ def __init__(self, cacheItemID, cacheStore):
+ self._cacheItemID = cacheItemID
+ self._cacheStore = cacheStore
+ self._refreshTime = None
+ self._expiryTime = 0
+
+ def hasExpired(self):
+ return (self._expiryTime and time.time() > self._expiryTime)
+
+ def setExpiryTime(self, time):
+ self._expiryTime = time
+
+ def getExpiryTime(self):
+ return self._expiryTime
+
+ def setData(self, data):
+ self._refreshTime = time.time()
+ self._cacheStore.set(self._cacheItemID, data, self._expiryTime)
+
+ def getRefreshTime(self):
+ return self._refreshTime
+
+ def getData(self):
+ assert self._refreshTime
+ return self._cacheStore.get(self._cacheItemID)
+
+ def renderOutput(self):
+ """Can be overridden to implement edge-caching"""
+ return self.getData() or ""
+
+ def clear(self):
+ self._cacheStore.delete(self._cacheItemID)
+ self._refreshTime = None
+
+class _CacheDataStoreWrapper(object):
+ def __init__(self, dataStore, keyPrefix):
+ self._dataStore = dataStore
+ self._keyPrefix = keyPrefix
+
+ def get(self, key):
+ return self._dataStore.get(self._keyPrefix+key)
+
+ def delete(self, key):
+ self._dataStore.delete(self._keyPrefix+key)
+
+ def set(self, key, val, time=0):
+ self._dataStore.set(self._keyPrefix+key, val, time=time)
+
+class CacheRegion(object):
+ '''
+ A `CacheRegion` stores some `CacheItem` instances.
+
+ This implementation stores the data in the memory of the current process.
+ If you need a more advanced data store, create a cacheStore class that works
+ with Cheetah's CacheStore protocol and provide it as the cacheStore argument
+ to __init__. For example you could use
+ Cheetah.CacheStore.MemcachedCacheStore, a wrapper around the Python
+ memcached API (http://www.danga.com/memcached).
+ '''
+ _cacheItemClass = CacheItem
+
+ def __init__(self, regionID, templateCacheIdPrefix='', cacheStore=None):
+ self._isNew = True
+ self._regionID = regionID
+ self._templateCacheIdPrefix = templateCacheIdPrefix
+ if not cacheStore:
+ cacheStore = Cheetah.CacheStore.MemoryCacheStore()
+ self._cacheStore = cacheStore
+ self._wrappedCacheDataStore = _CacheDataStoreWrapper(
+ cacheStore, keyPrefix=templateCacheIdPrefix+':'+regionID+':')
+ self._cacheItems = {}
+
+ def isNew(self):
+ return self._isNew
+
+ def clear(self):
+ " drop all the caches stored in this cache region "
+ for cacheItemId in self._cacheItems.keys():
+ cacheItem = self._cacheItems[cacheItemId]
+ cacheItem.clear()
+ del self._cacheItems[cacheItemId]
+
+ def getCacheItem(self, cacheItemID):
+ """ Lazy access to a cacheItem
+
+ Try to find a cache in the stored caches. If it doesn't
+ exist, it's created.
+
+ Returns a `CacheItem` instance.
+ """
+ cacheItemID = md5(str(cacheItemID)).hexdigest()
+
+ if cacheItemID not in self._cacheItems:
+ cacheItem = self._cacheItemClass(
+ cacheItemID=cacheItemID, cacheStore=self._wrappedCacheDataStore)
+ self._cacheItems[cacheItemID] = cacheItem
+ self._isNew = False
+ return self._cacheItems[cacheItemID]
View
@@ -0,0 +1,106 @@
+'''
+Provides several CacheStore backends for Cheetah's caching framework. The
+methods provided by these classes have the same semantics as those in the
+python-memcached API, except for their return values:
+
+set(key, val, time=0)
+ set the value unconditionally
+add(key, val, time=0)
+ set only if the server doesn't already have this key
+replace(key, val, time=0)
+ set only if the server already have this key
+get(key, val)
+ returns val or raises a KeyError
+delete(key)
+ deletes or raises a KeyError
+'''
+import time
+
+class Error(Exception):
+ pass
+
+class AbstractCacheStore(object):
+
+ def set(self, key, val, time=None):
+ raise NotImplementedError
+
+ def add(self, key, val, time=None):
+ raise NotImplementedError
+
+ def replace(self, key, val, time=None):
+ raise NotImplementedError
+
+ def delete(self, key):
+ raise NotImplementedError
+
+ def get(self, key):
+ raise NotImplementedError
+
+class MemoryCacheStore(AbstractCacheStore):
+ def __init__(self):
+ self._data = {}
+
+ def set(self, key, val, time=0):
+ self._data[key] = (val, time)
+
+ def add(self, key, val, time=0):
+ if key in self._data:
+ raise Error('a value for key %r is already in the cache'%key)
+ self._data[key] = (val, time)
+
+ def replace(self, key, val, time=0):
+ if key in self._data:
+ raise Error('a value for key %r is already in the cache'%key)
+ self._data[key] = (val, time)
+
+ def delete(self, key):
+ del self._data[key]
+
+ def get(self, key):
+ (val, exptime) = self._data[key]
+ if exptime and time.time() > exptime:
+ del self._data[key]
+ raise KeyError(key)
+ else:
+ return val
+
+ def clear(self):
+ self._data.clear()
+
+class MemcachedCacheStore(AbstractCacheStore):
+ servers = ('127.0.0.1:11211')
+ def __init__(self, servers=None, debug=False):
+ if servers is None:
+ servers = self.servers
+ from memcache import Client as MemcachedClient
+ self._client = MemcachedClient(servers, debug)
+
+ def set(self, key, val, time=0):
+ self._client.set(key, val, time)
+
+ def add(self, key, val, time=0):
+ res = self._client.add(key, val, time)
+ if not res:
+ raise Error('a value for key %r is already in the cache'%key)
+ self._data[key] = (val, time)
+
+ def replace(self, key, val, time=0):
+ res = self._client.replace(key, val, time)
+ if not res:
+ raise Error('a value for key %r is already in the cache'%key)
+ self._data[key] = (val, time)
+
+ def delete(self, key):
+ res = self._client.delete(key, time=0)
+ if not res:
+ raise KeyError(key)
+
+ def get(self, key):
+ val = self._client.get(key)
+ if val is None:
+ raise KeyError(key)
+ else:
+ return val
+
+ def clear(self):
+ self._client.flush_all()
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+import os
+import pprint
+
+try:
+ from functools import reduce
+except ImportError:
+ # Assume we have reduce
+ pass
+
+from Cheetah import Parser
+from Cheetah import Compiler
+from Cheetah import Template
+
+class Analyzer(Parser.Parser):
+ def __init__(self, *args, **kwargs):
+ self.calls = {}
+ super(Analyzer, self).__init__(*args, **kwargs)
+
+ def eatDirective(self):
+ directive = self.matchDirective()
+ try:
+ self.calls[directive] += 1
+ except KeyError:
+ self.calls[directive] = 1
+ super(Analyzer, self).eatDirective()
+
+class AnalysisCompiler(Compiler.ModuleCompiler):
+ parserClass = Analyzer
+
+
+def analyze(source):
+ klass = Template.Template.compile(source, compilerClass=AnalysisCompiler)
+ return klass._CHEETAH_compilerInstance._parser.calls
+
+def main_file(f):
+ fd = open(f, 'r')
+ try:
+ print u'>>> Analyzing %s' % f
+ calls = analyze(fd.read())
+ return calls
+ finally:
+ fd.close()
+
+
+def _find_templates(directory, suffix):
+ for root, dirs, files in os.walk(directory):
+ for f in files:
+ if not f.endswith(suffix):
+ continue
+ yield root + os.path.sep + f
+
+def _analyze_templates(iterable):
+ for template in iterable:
+ yield main_file(template)
+
+def main_dir(opts):
+ results = _analyze_templates(_find_templates(opts.dir, opts.suffix))
+ totals = {}
+ for series in results:
+ if not series:
+ continue
+ for k, v in series.iteritems():
+ try:
+ totals[k] += v
+ except KeyError:
+ totals[k] = v
+ return totals
+
+
+def main():
+ from optparse import OptionParser
+ op = OptionParser()
+ op.add_option('-f', '--file', dest='file', default=None,
+ help='Specify a single file to analyze')
+ op.add_option('-d', '--dir', dest='dir', default=None,
+ help='Specify a directory of templates to analyze')
+ op.add_option('--suffix', default='tmpl', dest='suffix',
+ help='Specify a custom template file suffix for the -d option (default: "tmpl")')
+ opts, args = op.parse_args()
+
+ if not opts.file and not opts.dir:
+ op.print_help()
+ return
+
+ results = None
+ if opts.file:
+ results = main_file(opts.file)
+ if opts.dir:
+ results = main_dir(opts)
+
+ pprint.pprint(results)
+
+
+if __name__ == '__main__':
+ main()
+
View
@@ -0,0 +1,16 @@
+import Cheetah.Template
+
+def render(template_file, **kwargs):
+ '''
+ Cheetah.Django.render() takes the template filename
+ (the filename should be a file in your Django
+ TEMPLATE_DIRS)
+
+ Any additional keyword arguments are passed into the
+ template are propogated into the template's searchList
+ '''
+ import django.http
+ import django.template.loader
+ source, loader = django.template.loader.find_template_source(template_file)
+ t = Cheetah.Template.Template(source, searchList=[kwargs])
+ return django.http.HttpResponse(t.__str__())
Oops, something went wrong.

0 comments on commit 8f32f04

Please sign in to comment.