Better caching mechanics

This commit is contained in:
斟酌 鵬兄 2022-08-02 22:35:37 +09:00
parent 00180c815d
commit 70618b6b91
7 changed files with 104 additions and 38 deletions

View File

@ -1,11 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os; import os, re, sys
import re;
import sys;
from xml.dom import minidom from xml.dom import minidom
from collections import defaultdict from collections import defaultdict
from botanjs.utils import checksum
RegEx_N = re.compile( r""" RegEx_N = re.compile( r"""
.* .*
@ -136,7 +135,7 @@ class ClassMap:
return True; return True;
def drawMap( self, ns, ci, ce, cf ): def drawMap( self, ns, ci, ce, cf, chksum ):
nsNode = self.getNode( ns ) nsNode = self.getNode( ns )
# Source every: # Source every:
@ -150,6 +149,8 @@ class ClassMap:
if not srcEvery: if not srcEvery:
nsNode.setAttribute( "src", cf ) nsNode.setAttribute( "src", cf )
nsNode.setAttribute( "js", chksum["js"] )
nsNode.setAttribute( "css", chksum["css"] )
for ex in ce: for ex in ce:
_t = eDef[ ex[0] ] _t = eDef[ ex[0] ]
@ -164,6 +165,8 @@ class ClassMap:
cNode.appendChild( impNode ) cNode.appendChild( impNode )
cNode.setAttribute( "src", cf ) cNode.setAttribute( "src", cf )
cNode.setAttribute( "js", chksum["js"] )
cNode.setAttribute( "css", chksum["css"] )
# the file dose not export classes # the file dose not export classes
# Hence it import for itself # Hence it import for itself
@ -187,7 +190,12 @@ class ClassMap:
continue continue
ns, ci, ce = classMeta( classFile ) ns, ci, ce = classMeta( classFile )
chksum = {}
chksum[ "js" ] = checksum( classFile )
chksum[ "css" ] = checksum( classFile[:-2] + "css" )
classFile = classFile.replace( self.R + os.path.sep, "" ) classFile = classFile.replace( self.R + os.path.sep, "" )
self.drawMap( ns, ci, ce, classFile ) self.drawMap( ns, ci, ce, classFile, chksum )
return self.DOM.toxml() return self.DOM.toxml()

View File

@ -4,11 +4,11 @@ import os
from sys import platform from sys import platform
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from botanjs.config import Config as config from botanjs.config import Config as config
from botanjs.service.jwork import log
COMPILER = config[ "BotanJS" ][ "ClosureCompiler" ] COMPILER = config[ "BotanJS" ][ "ClosureCompiler" ]
if not os.path.isfile( COMPILER ): AVAILABLE = os.path.isfile( COMPILER )
raise Exception( "Compiler not found" )
COMPILER_OPTIONS = [ COMPILER_OPTIONS = [
@ -42,6 +42,11 @@ class Wrapper:
break break
def compress( self, loc ): def compress( self, loc ):
if not AVAILABLE:
log.error( "Compiler not found" )
return
content = "" content = ""
with open( loc, "rb" ) as f: with open( loc, "rb" ) as f:
content = f.read() content = f.read()

View File

@ -1,7 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
class log: class log:
def info( self, *args ):
@staticmethod
def info( *args ):
print( *args )
@staticmethod
def error( *args ):
print( *args ) print( *args )
class dummyTask( object ): class dummyTask( object ):

View File

@ -30,7 +30,11 @@ class Resolver:
def resource( self, elem ): def resource( self, elem ):
if "src" in elem.attrib: if "src" in elem.attrib:
return elem.attrib[ "src" ] return {
"src": elem.attrib[ "src" ]
, "js": elem.attrib[ "js" ]
, "css": elem.attrib[ "css" ]
}
parent = self.parentMap[ elem ] parent = self.parentMap[ elem ]
@ -153,9 +157,10 @@ class BotanClassResolver:
if src not in classFiles: if src not in classFiles:
classFiles.append( src ) classFiles.append( src )
def cssLookup( self, jsList, cssList ): def cssLookup( self, classList, cssList ):
for f in jsList: for cdef in classList:
f = cdef[ "src" ]
possibleList = [] possibleList = []
cssFile = os.path.splitext( f )[0] + ".css" cssFile = os.path.splitext( f )[0] + ".css"
@ -183,7 +188,7 @@ class BotanClassResolver:
if self.CR == None: if self.CR == None:
return None return None
md5 = hashlib.md5( bytearray( "".join( fileList ), "utf-8" ) ).hexdigest() md5 = hashlib.md5( bytearray( "|".join( x[mode] for x in fileList ), "utf-8" ) ).hexdigest()
cName[0] = oFHash = md5 + "." + mode cName[0] = oFHash = md5 + "." + mode
cFHash = md5 + ".c." + mode cFHash = md5 + ".c." + mode
@ -193,19 +198,10 @@ class BotanClassResolver:
# Compressed file # Compressed file
cFile = os.path.join( self.CR, cFHash ) cFile = os.path.join( self.CR, cFHash )
dates = list( if self.flagCompress and self.useCache( cFile ):
os.path.getmtime( os.path.join( self.R, x ) )
if os.path.exists( os.path.join( self.R, x ) ) else -1
for x in fileList
)
# Root file date
dates.append( os.path.getmtime( os.path.join( self.R, "_this.js" ) ) );
if self.flagCompress and self.useCache( cFile, dates ):
return self.BotanCache( cFile, cFHash, self.returnDynamic ) return self.BotanCache( cFile, cFHash, self.returnDynamic )
elif self.useCache( oFile, dates ): elif self.useCache( oFile ):
self.JWork.saveCache( self.JWork.saveCache(
oFile oFile
# Content is None to initiate a compression # Content is None to initiate a compression
@ -216,17 +212,8 @@ class BotanClassResolver:
return self.BotanCache( oFile, oFHash, False ) return self.BotanCache( oFile, oFHash, False )
def useCache( self, f, dList ): def useCache( self, f ):
if not os.path.exists( f ): return os.path.exists( f )
return False
t = os.path.getmtime( f )
for i in dList:
if t < i:
return False
return True
def compileJs( self, cList, xList ): def compileJs( self, cList, xList ):
md5 = [ None ] md5 = [ None ]
@ -244,6 +231,7 @@ class BotanClassResolver:
for f in cList: for f in cList:
f = f[ "src" ]
path = ( path = (
os.path.splitext( f )[0] os.path.splitext( f )[0]
.replace( PY_SEP, "." ) .replace( PY_SEP, "." )
@ -285,8 +273,8 @@ class BotanClassResolver:
if cacheFile != None: if cacheFile != None:
return cacheFile; return cacheFile;
# The root file struct = "/* @ */"
outputCss = self.BotanFile( "_this.css" ) outputCss = struct + self.BotanFile( "_this.css" )
for f in self.cleanList( cList ): for f in self.cleanList( cList ):
outputCss += self.BotanFile( f ) outputCss += self.BotanFile( f )

View File

@ -1,7 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
from botanjs.compressor.closure import Wrapper as ClosureWrapper
from botanjs.compressor.yui import Wrapper as YUIWrapper
from botanjs.classmap import ClassMap from botanjs.classmap import ClassMap
CeleryExists = True CeleryExists = True
@ -38,6 +36,7 @@ class JWork:
@app.task() @app.task()
def compressJs( md5, externs ): def compressJs( md5, externs ):
from botanjs.compressor.closure import Wrapper as ClosureWrapper
log.info( "Compress js: " + md5 ) log.info( "Compress js: " + md5 )
w = ClosureWrapper() w = ClosureWrapper()
w.scanExterns( externs ) w.scanExterns( externs )
@ -45,6 +44,7 @@ class JWork:
@app.task() @app.task()
def compressCss( md5 ): def compressCss( md5 ):
from botanjs.compressor.yui import Wrapper as YUIWrapper
log.info( "Compress css: " + md5 ) log.info( "Compress css: " + md5 )
w = YUIWrapper() w = YUIWrapper()
w.compress( md5 ) w.compress( md5 )

12
botanjs/utils.py Normal file
View File

@ -0,0 +1,12 @@
from functools import lru_cache
from zlib import adler32 as _HashFunc
HashFunc = lambda v: hex( _HashFunc( v ) )[2:]
@lru_cache( maxsize = 1024 )
def checksum( file_path ):
try:
with open( file_path, "rb" ) as f:
return HashFunc( f.read() )
except FileNotFoundError:
return HashFunc( b"" )

47
tests.py Normal file
View File

@ -0,0 +1,47 @@
#!env/bin/python
import os, sys
sys.path.append( os.path.abspath( "." ) )
from botanjs.service.jwork import app, JWork
from botanjs.config import Config as config
SiteRoot = os.path.abspath( "." )
# Setting the SiteRoot for config
config["Paths"]["SiteRoot"] = SiteRoot
jsCache = config["Paths"]["Cache"]
jsRoot = config["BotanJS"]["SrcDir"]
bmap = os.path.join( jsCache, "botanjs", "bmap.xml" )
app.conf.update( broker_url = config["BotanJS"]["CeleryBroker"] )
JWork.buildClassMap.delay( jsRoot, bmap )
from botanjs.service.jclassresv import BotanClassResolver as JCResv
srvHandler = JCResv( JWork, jsRoot, bmap, jsCache )
import unittest
class TestStringMethods( unittest.TestCase ):
# Run each twice to test the cache capabilities
def test_ojscall( self ):
for _ in range(0,2):
s = srvHandler.getAPI( "System", mode = "rjs" )
self.assertTrue( "BotanJS.define( \"System\" );" in s )
def test_import( self ):
for _ in range(0,2):
s = srvHandler.getAPI( "System.Policy", mode = "rjs" )
self.assertTrue( "BotanJS.define( \"System.Policy\" );" in s )
self.assertTrue( "BotanJS.define( \"System.Global\" );" in s )
def test_cssInheritance( self ):
for _ in range(0,2):
s = srvHandler.getAPI( "System", mode = "rcss" )
self.assertTrue( "/* @ */" in s )
if __name__ == '__main__':
unittest.main()