ink.pyx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. # cython: profile=False
  4. #------------------------------------------------------------------------------
  5. # The Ink Templating System
  6. # A lightweight, fast, flexible text templating system
  7. # Copyright 2014 Christopher Simpkins
  8. # MIT License
  9. #------------------------------------------------------------------------------
  10. import re
  11. #------------------------------------------------------------------------------
  12. # Template class
  13. # A template string class that is inherited from Python str
  14. # Includes metadata about the template:
  15. # odel = opening delimiter
  16. # cdel = closing delimiter
  17. # varlist = inclusive list of all variables in the template text (parsed in constructor)
  18. # Delimiters:
  19. # default = {{variable}}
  20. # assign new opening and closing delimiters as parameters when you make a new Template instance
  21. # `escape_regex` boolean is a speedup, avoids Python escape of special regex chars if you do not need it
  22. #------------------------------------------------------------------------------
  23. class Template(str):
  24. def __new__(cls, template_text, open_delimiter="{{", close_delimiter="}}", escape_regex=False):
  25. obj = str.__new__(cls, template_text)
  26. obj.odel = open_delimiter
  27. obj.cdel = close_delimiter
  28. obj.varlist = obj._make_var_list(template_text, escape_regex) #contains all unique parsed variables from the template in a list
  29. return obj
  30. #------------------------------------------------------------------------------
  31. # [ _make_var_list method ] (list of strings)
  32. # Private method that parses the template string for all variables that match the delimiter pattern
  33. # Returns a list of the variable names as strings
  34. #------------------------------------------------------------------------------
  35. def _make_var_list(self, template_text, escape_regex=False):
  36. if escape_regex:
  37. open_match_pat = self._escape_regex_special_chars(self.odel)
  38. close_match_pat = self._escape_regex_special_chars(self.cdel)
  39. match_pat = open_match_pat + r'(.*?)' + close_match_pat # capture group contains the variable name used between the opening and closing delimiters
  40. else:
  41. match_pat = self.odel + r'(.*?)' + self.cdel
  42. var_list = re.findall(match_pat, template_text) #generate a list that contains the capture group from the matches (i.e. the variables in the template)
  43. return set(var_list) # remove duplicate entries by converting to set (and lookup speed improvement from hashing)
  44. #------------------------------------------------------------------------------
  45. # [ _escape_regex_special_chars method ] (string)
  46. # Private method that escapes special regex metacharacters
  47. # Returns a string with the escaped character modifications
  48. #------------------------------------------------------------------------------
  49. def _escape_regex_special_chars(self, test_escape_string):
  50. return re.escape(test_escape_string)
  51. #------------------------------------------------------------------------------
  52. # Renderer class
  53. # Render the variable replacements in the ink template using a Python dictionary key argument
  54. # Construct the instace of the Renderer with the Ink template and the dictionary key
  55. # Run the renderer with the render method on the instance (e.g. r.render())
  56. # Parameters to constructor:
  57. # - template = an Ink Template instance
  58. # - key = a dictionary mapped key = variable name : value = variable replacement data
  59. # - html_entities = encode html entities with HTML escaped characters (default = False = do not encode)
  60. #------------------------------------------------------------------------------
  61. class Renderer:
  62. def __init__(self, template, key, html_entities=False):
  63. self.odel = template.odel
  64. self.cdel = template.cdel
  65. self.template = template
  66. self.html_entities = html_entities
  67. self.key_dict = key
  68. #------------------------------------------------------------------------------
  69. # [ render method ] (string)
  70. # renders the variable replacements in the Ink template
  71. # returns the rendered template as a string
  72. #------------------------------------------------------------------------------
  73. def render(self):
  74. # make local variables for the loop below (faster)
  75. local_dict = self.key_dict
  76. local_template = self.template
  77. local_varlist = self.template.varlist
  78. local_odel = self.odel
  79. local_cdel = self.cdel
  80. local_htmlent = self.html_entities
  81. if local_htmlent:
  82. from xml.sax.saxutils import escape #from Python std lib
  83. for key in local_dict:
  84. if key in local_varlist:
  85. value = local_dict[key]
  86. replace_string = local_odel + key + local_cdel
  87. if local_htmlent:
  88. value = escape(value) #xml.sax.saxutils function
  89. local_template = local_template.replace(replace_string, value)
  90. return local_template
  91. ##TODO : multiple file render method?
  92. if __name__ == '__main__':
  93. pass
  94. # template = Template("This is a of the {{test}} of the {{document}} {{type}} and more of the {{test}} {{document}} {{type}}")
  95. # renderer = Renderer(template, {'test': 'ব য', 'document':'testing document', 'type':'of mine', 'bogus': 'bogus test'})
  96. # print(renderer.render())