removed main.cfg, not used anywhere
[limo.git] / cache.py
blob51aed240164e8e8f7cf2f1c0b845d9ea95c9faea
1 from __future__ import with_statement
2 import hashlib, os
3 from limoutil import *
4 from settings import Settings
6 class _AggreggateFileSet:
7 """ Provides a base class for aggreggating text files.
9 First generate some test content.
11 >>> import os
12 >>> if os.path.isfile(Settings.tempDir+"/test.js"): os.remove(Settings.tempDir+"/test.js")
13 >>> o = open(Settings.tempDir+"/test.js").write("function func1() { \nalert('with lots of space');}")
14 >>> o.close()
15 >>> if os.path.isfile(Settings.tempDir+"/test2.js"): os.remove(Settings.tempDir+"/test2.js")
16 >>> o = open(Settings.tempDir+"/test2.js").write("function func2() { alert('this is function 2');}")
17 >>> o.close()
19 Then use defaultFile() to load the test content into the file set.
21 >>> Cache.JS.defaultFile(Settings.tempDir+"/test.js")
22 >>> Cache.JS.defaultFile(Settings.tempDir+"/test2.js")
23 >>> hash = hashlib.sha1(''.join([Settings.tempDir+"/test.js",Settings.tempDir+"/test2.js"])).hexdigest()
24 >>> assert(Cache.JS.getOutputFile().startswith('cache/'+hash))
25 >>> Cache.JS.saveContent()
26 >>> assert(os.path.isfile(Cache.JS.getOutputFile())
27 >>> Cache.JS.getText()
28 'function func1(){alert('with lots of space');}function func2(){alert('this is function 2');}'
29 """
31 def __init__(self):
32 self._order = []
33 self._data = {}
34 self._mtimes = {}
36 def defaultFile(self, file):
37 if file is None or not os.path.isfile(file):
38 if Settings.abortOnMissingFile:
39 raise HTTPStatusError(404, "Required asset is missing: %s" % file)
40 if Settings.warnOnMissingFile:
41 log("WARNING: File Cache could not load requested file: %s" % file)
42 if self._data.get(file, False): # dont add the same file twice
43 if os.path.getmtime(file) > self._mtimes.get(file, 0): # unless its been modified
44 log("Reading file from disk into cache: %s" % file)
45 else:
46 # log("Skipping read of unmodified file: %s %d <= %d " % (file, os.path.getmtime(file), self._mtimes.get(file, 0)))
47 return
48 if file is not None and os.path.isfile(file):
49 self._order.append(file)
50 # TODO: lazy reading, only read() if, in the end, the whole request needs to be written into the cache
51 self._mtimes[file] = os.path.getmtime(file)
52 with open(file,"rb") as f:
53 self._data[file] = f.read()
55 def getOutputFile(self):
56 """ Subclasses should implement this to add the proper extension """
57 fname_hash = hashlib.sha1(''.join(self._order)).hexdigest()
58 mtime_hash = hashlib.sha1(''.join([str(os.path.getmtime(f)) for f in self._order])).hexdigest()
59 return Settings.cacheDir+os.path.sep+"%s-%s" %(fname_hash, mtime_hash)
61 def saveContent(self):
62 f = self.getOutputFile()
63 if not os.path.isfile(f):
64 # remove stale cache files first
65 """DISABLED
66 key = f[6:10]
67 for file in os.listdir(Settings.cacheDir):
68 if file == f:
69 file = os.path.sep.join([Settings.cacheDir,file])
70 log("Removing stale cache file: %s" % (file))
71 os.remove(file)
72 """
73 # then write the fresh one
74 log("Writing files: [%s] to cache: %s" % (', '.join(self.order()), self.getOutputFile()))
75 leader = "/* Files: "+', '.join(self.order())+" */\n"
76 packed = ''.join([self.filter(self._data[key]) for key in self.order()])
77 with open(self.getOutputFile(),'wb') as f:
78 f.write(leader)
79 f.write(packed)
81 def order(self):
82 return self._order
84 def clear(self):
85 self._order = []
86 self._data.clear()
88 def getText(self):
89 self.saveContent()
90 with open(z_file,"rb") as f:
91 return f.read()
93 def filter(self, text):
94 """ Subclasses should implement this in order to do minification, compression, etc. """
95 return text
97 import jsmin, cssmin
99 class _JSFileSet(_AggreggateFileSet):
100 def filter(self, text):
101 if Settings.developmentMode:
102 return text;
103 else:
104 return jsmin.jsmin(text)
105 def getOutputFile(self):
106 return _AggreggateFileSet.getOutputFile(self)+".js"
108 class _CSSFileSet(_AggreggateFileSet):
109 def filter(self, text):
110 if Settings.developmentMode:
111 return text;
112 else:
113 return cssmin.minimalize(text)
114 def getOutputFile(self):
115 return _AggreggateFileSet.getOutputFile(self)+".css"
116 def order(self):
117 return self._order
119 class Cache:
120 CSS = _CSSFileSet()
121 JS = _JSFileSet()