Python大神教你300行代码搞定HTML模板渲染附源码
一、前言
模板语言由HTML代码和逻辑控制代码组成,此处PHP。通过模板语言可以快速的生成预想的HTML页面。应该算是后端渲染不可缺少的组成部分。二、功能介绍
通过使用学习tornado、bottle的模板语言,我也效仿着实现可以独立使用的模板渲染的代码模块,模板语法来自tornado和bottle的语法。可以用来做一些简单的事情网页渲染,邮件内容生成等HTML显示方面。以下就是简单的语法使用介绍。
1。变量。使用{{}}包裹起来,里面的变量为Python传入。模板渲染时会将传入的变量转换成字符串并填入对应位置。模板文件内容title{{mytitle}}titlelabel{{session。name}}labelpy代码调用thtml为上面的内容Template(thtml)。render(mytitle标题,sessionsomeobj)
2。转义。默认传入的数据都会进行HTML转义,可以使用{rawvalue}来将value的内容按原始字符串输出。模板文件内容p{rawvalue}Py调用内容Template(thtml)。render(mytitlelabel显示标签label)
3。条件控制。支持Python的if,elif,else。条件代码需要放在{}内部,并且在条件结束后需要额外增加{end},用于标识条件控制语句块范围。模板文件内容{ifa1}labelA大于1label{else}labelA小于或等于1label{end}py调用Template(thtml)。render(a1)
4。循环控制。支持Python的for和while。与条件控制一样也需要放在{}内部,并且结束处需要额外增加{end},用于标识循环控制语句块的范围。模板文件内容{foriinrange(10)}label当前序号:{{i1}}label{end}py调用Template(thtml)。render()
5。变量声明。如果需要在模板文件内声明一个变量,方便使用时,可以通过set来实现。具体格式为{setvxx}。通过set声明的变量在整个模板文件中都可以使用,包括在条件控制和循环控制中作为条件判断也可以。模板文件内容{seta1}labela的值:{{a}}label三、源码
这个模板语言模块是在Python2。7上面开发使用的,如果要在Python3上使用需要对str和bytes进行一些处理即可,由于没有引用任何其他模块,可以很好地独立使用。1coding:utf823模板语言45TOKEN相关的定义6TOKENSBRACE{7TOKENSBLOCK8TOKENEXPRESSIONL{{9TOKENEXPRESSIONR}}10TOKENBLOCKL{11TOKENBLOCKR}12TOKENKEYSETset13TOKENKEYRAWraw14TOKENKEYIFif15TOKENKEYELIFelif16TOKENKEYELSEelse17TOKENKEYFORfor18TOKENKEYWHILEwhile19TOKENKEYENDend20TOKENKEYBREAKbreak21TOKENKEYCONTINUEcontinue22TOKENSPACE23TOKENCOLON:24Token标记{{}}{}25TOKENFLAGSET{TOKENSBRACE,TOKENSBLOCK}26简单的语句27TOKENKEYSETSIMPLEEXPRESSION{TOKENKEYSET,TOKENKEYRAW}28前置条件29TOKENKEYPRECONDITION{30end必须在ifelifelseforwhile后面31TOKENKEYEND:{TOKENKEYIF,TOKENKEYELIF,TOKENKEYELSE,32TOKENKEYFOR,TOKENKEYWHILE},33elif必须在if后面34TOKENKEYELIF:{TOKENKEYIF},35else必须在ifelif后面36TOKENKEYELSE:{TOKENKEYIF,TOKENKEYELIF,TOKENKEYFOR,TOKENKEYWHILE},37}38循环语句39TOKENKEYLOOP{TOKENKEYWHILE,TOKENKEYFOR}40循环的控制breakcontinue41TOKENKEYLOOPCTRL{TOKENKEYBREAK,TOKENKEYCONTINUE}4243classParseException(Exception):44pass4546classTemplateCode(object):47definit(self):48self。codeTrees{parent:None,nodes:〔〕}49self。cursorself。codeTrees50self。compiledcodeNone5152defcreatecode(self):53创建一个代码子块54childcodes{parent:self。cursor,nodes:〔〕}55self。cursor〔nodes〕。append(childcodes)56self。cursorchildcodes5758defclosecode(self):59关闭一个代码子块60assertself。cursor〔parent〕isnotNone,overflow61self。cursorself。cursor〔parent〕6263defappendtext(self,text):64添加文本65排除空行66self。cursor〔nodes〕。append(add(r)text)6768defappendexpress(self,express,rawFalse):69表达式70ifraw:71tempexptexpstr(s)express72else:73tempexptexpesc(s)express74self。cursor〔nodes〕。append(tempexp)75self。cursor〔nodes〕。append(add(texp))7677defappendstatement(self,statement):78语句79tempstatementsstatement80self。cursor〔nodes〕。append(tempstatement)8182defreset(self):83self。codeTrees{parent:None,nodes:〔〕}84self。cursorself。codeTrees85self。compiledcodeNone8687defbuildcode(self,filename):88tempcodebuff〔〕89self。writebuffwithindent(tempcodebuff,deftemplaterender():,0)90self。writebuffwithindent(tempcodebuff,codes〔〕,4)91self。writebuffwithindent(tempcodebuff,addcodes。append,4)92self。writecodes(tempcodebuff,self。codeTrees,4)93self。writebuffwithindent(tempcodebuff,return。join(codes),4)94tempcode。join(tempcodebuff)95self。compiledcodecompile(tempcode,filename,exec,dontinheritTrue)9697defwritecodes(self,codebuff,codes,indent):98fornodeincodes。get(nodes,〔〕):99ifisinstance(node,dict):100self。writecodes(codebuff,node,indent4)101else:102self。writebuffwithindent(codebuff,node,indent)103104defgenerate(self,kwargs):105tempnamespace{}106tempnamespace〔str〕self。toutf8107tempnamespace〔esc〕self。tosafeutf8108tempnamespace。update(kwargs)109exec(self。compiledcode,tempnamespace)110returntempnamespace〔templaterender〕()111112staticmethod113defwritebuffwithindent(codebuff,rawstr,indent):114115temp(indent)rawstr116codebuff。append(temp)117118staticmethod119deftoutf8(rawstr):120转换121ifisinstance(rawstr,str):122returnrawstr123elifisinstance(rawstr,bytes):124returnrawstr。decode()125returnstr(rawstr)126127staticmethod128deftosafeutf8(rawstr):129过滤html转义130textTemplateCode。toutf8(rawstr)131returntext。replace(,)。replace(,)。replace(,)132classTemplate(object):133模板类134definit(self,inputobj,filenamestring,namespace):135模板初始化136self。namespace{}137self。namespace。update(namespace)138将数据丢进去解析生成编译代码139self。lexerTemplateLexer(inputobj,filename)140141defrender(self,kwargs):142渲染模板143tempnamespace{}144tempnamespace。update(self。namespace)145tempnamespace。update(kwargs)146执行渲染147returnself。lexer。render(kwargs)148149classTemplateLexer(object):150模板语法分析器151definit(self,inputobb,filenamestring):152ifhasattr(inputobb,read):153self。rawstringinputobb。read()154else:155self。rawstringinputobb156self。filenamefilename157记录当前的位置158self。pos0159记录原始数据的总长度160self。rawstrlenlen(self。rawstring)161记录解析的数据162self。codedataTemplateCode()163开始解析164self。parsetemplate()165166defmatch(self,keyword,posNone):167returnself。rawstring。find(keyword,posifposisnotNoneelseself。pos)168169defcut(self,size1):170剪取数据size切割数据的大小,1表示全部171ifsize1:172newposself。rawstrlen173else:174newposself。possize175sself。rawstring〔self。pos:newpos〕176self。posnewpos177returns178179defremaining(self):180获取剩余大小181returnself。rawstrlenself。pos182183deffunctionbrace(self):184获取{{{185skipindexself。pos186whileTrue:187indexself。match(TOKENSBRACE,skipindex){{{188没找到189ifindex1:190returnNone,1191末尾192ifindexself。rawstrlen:193returnNone,1194匹配类型195nextvalueself。rawstring〔index1:index2〕196ifnextvaluenotinTOKENFLAGSET:197skipindexindex1198说明不是关键类型199continue200braceself。rawstring〔index:index2〕201returnbrace,index202returnNone,1203204defreadcontentwithtoken(self,index,begintoken,endtoken):205206读取匹配token的内容207208endindexself。match(endtoken)209ifendindex1:210returnParseException({0}missingendtoken{1}。format(begintoken,endtoken))211过滤begintoken212self。posindexlen(begintoken)213contentself。cut(endindexself。pos)214去除末尾endtoken215self。cut(len(endtoken))216returncontent217218defaddsimpleblockstatement(self,operator,suffix):219ifnotsuffix:220raiseParseException({0}missingcontent。format(operator))221ifoperatorTOKENKEYSET:222self。codedata。appendstatement(suffix)223elifoperatorTOKENKEYRAW:224self。codedata。appendexpress(suffix,True)225else:226raiseParseException({0}isundefined。format(operator))227228defparsetemplate(self):229解析模板230TODO检查模板文件是否更改过,如果没有则不需要重新解析231self。codedata。reset()232解析模板原文件233self。parse()234生成编译code235self。compiledcode()236237defrender(self,kwargs):238returnself。codedata。generate(kwargs)239240defparse(self,controloperatorNone,inloopFalse):241开始解析242whileTrue:243ifself。remaining()0:244ifcontroloperatororinloop:245raiseParseException(smissing{end}controloperator)246break247读取{{{248brace,indexself。functionbrace()249说明没有找到250ifnotbrace:251textself。cut(index)252self。codedata。appendtext(text)253continue254else:255textself。cut(indexself。pos)256iftext:257self。codedata。appendtext(text)258259ifbraceTOKENEXPRESSIONL:260contentself。readcontentwithtoken(index,TOKENEXPRESSIONL,TOKENEXPRESSIONR)。strip()261ifnotcontent:262raiseParseException(EmptyExpress)263self。codedata。appendexpress(content)264continue265elifbraceTOKENBLOCKL:266contentself。readcontentwithtoken(index,TOKENBLOCKL,TOKENBLOCKR)。strip()267ifnotcontent:268raiseParseException(Emptyblock)269270得到表达式forxinx;ifx;elifx;else;end;set;whilex;271operator,,suffixcontent。partition(TOKENSPACE)272ifnotoperator:273raiseParseException(blockmissingoperator)274275suffixsuffix。strip()276简单语句,setraw277ifoperatorinTOKENKEYSETSIMPLEEXPRESSION:278self。addsimpleblockstatement(operator,suffix)279elifoperatorinTOKENKEYLOOPCTRL:280ifnotinloop:281raiseParseException({0}mustinloopblock。format(operator))282self。codedata。appendstatement(operator)283else:284控制语句检查匹配if后面可以跟elifelse285preconditionTOKENKEYPRECONDITION。get(operator,None)286ifprecondition:287里面就是elifelseend288ifcontroloperatornotinprecondition:289raiseParseException({0}mustbehindwith{1}。format(operator,precondition))290elifoperatorTOKENKEYEND:291遇到{end}则结束292self。codedata。closecode()293return294else:295由于是依据if进入来计算elif,因此elif与if是同级的296self。codedata。closecode()297self。codedata。appendstatement(contentTOKENCOLON)298self。codedata。createcode()299self。parse(operator,inloopor(operatorinTOKENKEYLOOP))300break301添加控制语句及内部语句体ifforwhile302self。codedata。appendstatement(contentTOKENCOLON)303self。codedata。createcode()304self。parse(operator,inloopor(operatorinTOKENKEYLOOP))305else:306raiseParseException(Unkownbrace)307return308309defcompiledcode(self):310生成编译code311self。codedata。buildcode(self。filename)312ifnamemain:313tTemplate(html{{hello}}html)314t。render(hello你好
原文链接:
http:www。cnblogs。comjeffxunp15585073。html