通用Python 脚本生成模板引擎

-- Zoom.Quiet [2004-08-22 02:37:51]

1. 目标

040823 根据个人开发感受,整理模板实现思路

  • 这个好象没有详细定义过,所以造成了开发的理解混乱是也乎……

    • 模板代码应该与目标代码非常相近,以便根据目标代码的变化可以快速的修改;
    • 目标代码,模板的变化,模板解析类的修改应该非常少,或是不用修改!

2. 实现途径

2.1. XSLT

  • 如果目标代码变化不会过于复杂,还是首先考虑使用XSLT的方式!
  • 最标准最自然的XML解析模板语言!这样一来,Py脚本将非常简单,容易维护!
  • 4suite4xslt -- 040824 应用 4Suite 模块解决Otter模板问题

  • OtterSample -- 目标代码正式成为分项目

2.1.1. EXSLT

XSLT有些简单,复杂的数据操作还是不行,但是已经有新的标准进行扩展了!

2.2. 已有的模板系统

世上无难事,只怕有心人!只要找,总会找到的:

  • Python-based Template Packages

    • 可是实用的只有:
    • PTL

    • ZPT

  • 分析:
    • ZPT 好是好,但是只能在Zope 环境中运行?!
    • PTL 主要还是面向HTML的生成,而且有很怪的嵌入声明
  • http://www.nedbatchelder.com/code/cog/ 这是一个使用了python做为代码块的代码生成系统,已经达到了可以使用的状态

    • cog-web

    • 呜乎哀哉,是以C++为目标代码的!!还要重新清理结果哪!
  • http://www.jostraca.org/ 这是一个使用java开发的多语言代码生成工具

    • <% @import 
      import org.jostraca.resource.SimpleDataObject;
      import org.jostraca.resource.SimpleDataObjectReader;
      %>
      
    • 嗯嗯!需要大家习惯Java 狂冗长的丰富声明!!!

2.3. OtTool解析类

不食嗟来之食! 自力更生!创造自个儿的Py代码模板包!

2.3.1. 模板文件可以执行

2.3.2. 模板文件不用执行

  • 需要定义精巧的标签,使模板的写作,解析的维护都非常方便!
    • 就现在的实现方式来看,都是失败的…………

2.3.2.1. 小新的字典匹配

  • 一句话:
    • 以自定标签为准,将所有人工处理好的目标代码替换回填就好!
    • 是个力气活--每当模板,目标代码变化时

2.3.2.2. Zoomq的XML驱动

2.3.2.3. Limodou的set嵌套

   1 
   2 #coding=utf-8
   3 
   4 import re
   5 import sets
   6 import copy
   7 import types
   8 
   9 class T:
  10         def __init__(self, string):
  11                 self.text = string
  12 
  13         def getText(self):
  14                 return self.text
  15 
  16 #定义模板处理类
  17 class Template:
  18         def __init__(self, beginchars='<#', endchars='#>'):
  19                 self.beginchars = beginchars            #define template var's left delimeter chars
  20                 self.endchars = endchars                        #define template var's right delimeter chars
  21 
  22         #装入模板
  23         def load(self, tplname):
  24                 mod = __import__(tplname)
  25                 components = tplname.split('.')
  26                 for comp in components[1:]:
  27                         mod = getattr(mod, comp)
  28 
  29                 self.vars = {}
  30                 self.nodes = {}
  31                 for vn in dir(mod):
  32                         v = getattr(mod, vn)
  33                         if hasattr(v, '__class__') and v.__class__.__name__ == 'T':
  34                                 self.vars[vn] = v
  35                                 self.nodes[vn] = self._get_rely_on_node(v.getText())
  36 
  37         def _getPattern(self):
  38                 return r'%s(\w+)%s' % (self.beginchars, self.endchars)
  39 
  40         #取模板元素的相关集
  41         def _get_rely_on_node(self, s): #search for %(name)s format, make a dict
  42                 re_node = re.compile(self._getPattern())
  43 
  44                 return list(sets.Set(re_node.findall(s)))
  45 
  46         #取模板元素的替换顺序
  47         def _get_list(self, path, target):
  48                 if not self.vars.has_key(target):
  49                         return
  50                 if target not in path:
  51                         path.append(target)
  52                 for i in self.nodes[target]:
  53                         self._get_list(path, i)
  54                 return
  55 
  56         #生成模板值
  57         #values应为字典的字典。即每一个模板元素如果引用有外部的变量,那么在values中应有此模板元素的一个键。
  58         #同时它的值应为所有外部变量的一个字典
  59         def value(self, target='main', values=None):
  60                 path = []
  61                 self._get_list(path, target)
  62                 path.reverse()
  63                 vals = {}
  64                 for i in path:
  65                         value = self._getElementValue(i, vals, values)
  66                         vals[i] = value
  67                 return vals[target]
  68 
  69         #把一段文件本的可替换信息使用values中的变量进行替换
  70         #text是段字符串
  71         #values是一个对应替换信息的字典
  72         def _replace(self, text, values):
  73                 def dosup(matchobj, values=values):
  74                         if values:
  75                                 return values.get(matchobj.groups()[0], matchobj.group())
  76                         else:
  77                                 return matchobj.group()
  78                 return re.sub(self._getPattern(), dosup, text)
  79 
  80 
  81         #得到某个模板元素的替换值
  82         def _getElementValue(self, name, elements, values=None):
  83                 text = self.vars[name].getText()
  84                 #先将所有已经存在的模板元素进行替换
  85                 text = self._replace(text, elements)
  86                 #再根据外部引用变量的类型决定是否进行循环
  87                 if values and values.has_key(name):
  88                         if len(values[name]) == 1:
  89                                 text = self._replace(text, values[name])
  90                         else:
  91                                 s = []
  92                                 for v in values[name]:
  93                                         s.append(self._replace(text, v))
  94                                 text = ''.join(s)
  95 
  96                 return text
  97 
  98 if __name__ == '__main__':
  99         vars = dict(hello=[{'var':'var1'},{'var':'var2'},{'var':'var3'}])
 100         template = Template()
 101         template.load('tmp2')
 102         print template.value('program', vars)
  • 测试模板

from Template import T

hello = T("Hello, <#var#> \n")
message = T("Please input yourname:")
program = T("""name = input_raw("<#message#>")
print '''<#hello#>'''
        """)