forked from Botanical/BotanJS
		
	Better caching mechanics
This commit is contained in:
		@@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 ):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
									
								
							
							
						
						
									
										12
									
								
								botanjs/utils.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										47
									
								
								tests.py
									
									
									
									
									
										Normal 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()
 | 
				
			||||||
		Reference in New Issue
	
	Block a user