PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/pytof/ezt.py

http://pytof.googlecode.com/
Python | 206 lines | 136 code | 22 blank | 48 comment | 38 complexity | dcf5fdc25c605f42a0828c176f39dc00 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, GPL-3.0, GPL-2.0, IPL-1.0
  1. #
  2. # ezt.py -- easy templating
  3. #
  4. # Copyright (C) 2001 Greg Stein. All Rights Reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. #
  10. # * Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. #
  13. # * Redistributions in binary form must reproduce the above copyright
  14. # notice, this list of conditions and the following disclaimer in the
  15. # documentation and/or other materials provided with the distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  18. # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  19. # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  20. # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
  21. # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. # POSSIBILITY OF SUCH DAMAGE.
  28. #
  29. #
  30. # This software is maintained by Greg and is available at:
  31. # http://edna.sourceforge.net/
  32. #
  33. import string
  34. import re
  35. from types import StringType, IntType, FloatType
  36. _re_parse = re.compile('(\[[-\w.# ]+\])|(\[\[\])')
  37. # block commands and their argument counts
  38. _block_cmd_specs = { 'if-any':1, 'if-index':2, 'for':1 }
  39. _block_cmds = _block_cmd_specs.keys()
  40. class Template:
  41. def __init__(self, fname=None):
  42. if fname:
  43. self.parse_file(fname)
  44. def parse_file(self, fname):
  45. self.parse(open(fname).read())
  46. def parse(self, text):
  47. # parse the program into: (TEXT DIRECTIVE BRACKET)* TEXT
  48. # DIRECTIVE will be '[directive]' or None
  49. # BRACKET will be '[[]' or None
  50. parts = _re_parse.split(text)
  51. program = [ ]
  52. stack = [ ]
  53. for i in range(len(parts)):
  54. piece = parts[i]
  55. which = i % 3 # discriminate between: TEXT DIRECTIVE BRACKET
  56. if which == 0:
  57. # TEXT. append if non-empty.
  58. if piece:
  59. program.append(piece)
  60. elif which == 2:
  61. # BRACKET directive. append '[' if present.
  62. if piece:
  63. program.append('[')
  64. elif piece:
  65. # DIRECTIVE is present.
  66. args = string.split(piece[1:-1])
  67. cmd = args[0]
  68. if cmd == '#':
  69. # comment
  70. continue
  71. if cmd == 'else':
  72. if len(args) > 1:
  73. raise ArgCountSyntaxError()
  74. ### check: don't allow for 'for' cmd
  75. idx = stack[-1][1]
  76. true_section = program[idx:]
  77. del program[idx:]
  78. stack[-1][3] = true_section
  79. elif cmd == 'end':
  80. if len(args) > 1:
  81. raise ArgCountSyntaxError()
  82. # note: true-section may be None
  83. cmd, idx, args, true_section = stack.pop()
  84. else_section = program[idx:]
  85. func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
  86. program[idx:] = [ (func, (args, true_section, else_section)) ]
  87. elif cmd in _block_cmds:
  88. if len(args) > _block_cmd_specs[cmd] + 1:
  89. raise ArgCountSyntaxError()
  90. ### this assumes arg1 is always a ref
  91. args[1] = _prepare_ref(args[1])
  92. # remember the cmd, current pos, args, and a section placeholder
  93. stack.append([cmd, len(program), args[1:], None])
  94. else:
  95. # implied PRINT command
  96. if len(args) > 1:
  97. raise ArgCountSyntaxError()
  98. program.append((self._cmd_print, _prepare_ref(args[0])))
  99. self.program = program
  100. def generate(self, fp, data):
  101. ctx = _context()
  102. ctx.data = data
  103. ctx.for_index = { }
  104. self._execute(self.program, fp, ctx)
  105. def _execute(self, program, fp, ctx):
  106. for step in program:
  107. if isinstance(step, StringType):
  108. fp.write(step)
  109. else:
  110. step[0](step[1], fp, ctx)
  111. def _cmd_print(self, (refname, ref), fp, ctx):
  112. ### type check the value
  113. fp.write(_get_value(refname, ref, ctx))
  114. def _cmd_if_any(self, args, fp, ctx):
  115. "If the value is a non-empty string or non-empty list, then T else F."
  116. (((refname, ref),), t_section, f_section) = args
  117. value = _get_value(refname, ref, ctx)
  118. self._do_if(value, t_section, f_section, fp, ctx)
  119. def _cmd_if_index(self, args, fp, ctx):
  120. (((refname, ref), value), t_section, f_section) = args
  121. list, idx = ctx.for_index[refname]
  122. if value == 'even':
  123. value = idx % 2 == 0
  124. elif value == 'odd':
  125. value = idx % 2 == 1
  126. elif value == 'last':
  127. value = idx == len(list)-1
  128. else:
  129. value = idx == int(value)
  130. self._do_if(value, t_section, f_section, fp, ctx)
  131. def _do_if(self, value, t_section, f_section, fp, ctx):
  132. if t_section is None:
  133. t_section = f_section
  134. f_section = None
  135. if value:
  136. section = t_section
  137. else:
  138. section = f_section
  139. if section is not None:
  140. self._execute(section, fp, ctx)
  141. def _cmd_for(self, args, fp, ctx):
  142. (((refname, ref),), unused, section) = args
  143. list = _get_value(refname, ref, ctx)
  144. if isinstance(list, StringType):
  145. raise NeedSequenceError()
  146. ctx.for_index[refname] = [ list, 0 ]
  147. for i in range(len(list)):
  148. ctx.for_index[refname][1] = i
  149. self._execute(section, fp, ctx)
  150. del ctx.for_index[refname]
  151. def _prepare_ref(refname):
  152. return refname, string.split(refname, '.')
  153. def _get_value(refname, ref, ctx):
  154. if ctx.for_index.has_key(ref[0]):
  155. list, idx = ctx.for_index[ref[0]]
  156. ob = list[idx]
  157. elif ctx.data.has_key(ref[0]):
  158. ob = ctx.data[ref[0]]
  159. else:
  160. raise UnknownReference(refname)
  161. # walk the dotted ref
  162. for attr in ref[1:]:
  163. try:
  164. ob = getattr(ob, attr)
  165. except AttributeError:
  166. raise UnknownReference(refname)
  167. # make sure we return a string. ### other types?
  168. if isinstance(ob, IntType) or isinstance(ob, FloatType):
  169. return str(ob)
  170. if ob is None:
  171. return ''
  172. return ob
  173. class _context:
  174. pass
  175. class ArgCountSyntaxError(Exception):
  176. pass
  177. class UnknownReference(Exception):
  178. pass
  179. class NeedSequenceError(Exception):
  180. pass