PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/kid-0.9.6/kid/template_util.py

#
Python | 281 lines | 262 code | 12 blank | 7 comment | 37 complexity | 91d2827b64273f29fa31e88be8639952 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. """Utility functions used by generated kid modules."""
  3. __revision__ = "$Rev: 492 $"
  4. __date__ = "$Date: 2007-07-06 21:38:45 -0400 (Fri, 06 Jul 2007) $"
  5. __author__ = "Ryan Tomayko (rtomayko@gmail.com)"
  6. __copyright__ = "Copyright 2004-2005, Ryan Tomayko"
  7. __license__ = "MIT <http://www.opensource.org/licenses/mit-license.php>"
  8. import inspect
  9. import sys
  10. from types import TypeType, ModuleType, ClassType, GeneratorType
  11. import itertools
  12. # these are for use by template code
  13. import kid
  14. from kid.parser import XML, document, ElementStream, START, END, TEXT, \
  15. START_NS, COMMENT, PI, DOCTYPE, XML_DECL, to_unicode
  16. from kid.element import Element, SubElement, Comment, ProcessingInstruction
  17. __all__ = ['XML', 'document', 'ElementStream',
  18. 'Element', 'SubElement', 'Comment', 'ProcessingInstruction',
  19. 'START', 'END', 'TEXT', 'START_NS', 'COMMENT', 'PI',
  20. 'DOCTYPE', 'XML_DECL']
  21. class TemplateError(Exception): pass
  22. class TemplateNotFound(TemplateError): pass
  23. class TemplateImportError(TemplateError): pass
  24. class TemplateDictError(TemplateError): pass
  25. class TemplateAttrsError(TemplateError): pass
  26. class TemplateExtendsError(TemplateError): pass
  27. class TemplateLayoutError(TemplateError): pass
  28. _local_excludes = ['generate', 'module', 'parser', 'serialize', 'transform', 'write']
  29. def get_locals(inst, _locals=None):
  30. if _locals is None:
  31. _locals = {}
  32. ls = []
  33. local_excludes = _local_excludes # local copy
  34. for var, value in inspect.getmembers(inst):
  35. if not var.startswith('_') and not var in local_excludes \
  36. and var not in _locals:
  37. ls.append('%s=self.%s' % (var, var))
  38. return ';'.join(ls)
  39. def get_base_class(thing, from_file=None, arg=None):
  40. """Get template base class for thing, raising an exception on error."""
  41. if thing is None:
  42. return kid.BaseTemplate
  43. if isinstance(thing, TypeType):
  44. return thing
  45. elif isinstance(thing, ModuleType):
  46. try:
  47. cls = thing.Template
  48. except AttributeError:
  49. cls = None
  50. if (isinstance(cls, TypeType)
  51. and issubclass(cls, kid.BaseTemplate)
  52. and cls != kid.Template):
  53. return cls
  54. thing = repr(thing)
  55. if arg:
  56. thing = arg
  57. else:
  58. try:
  59. thing = thing.__name__
  60. except AttributeError:
  61. thing = repr(thing)
  62. raise TemplateNotFound(
  63. '%s is a module without Template class' % thing)
  64. elif isinstance(thing, basestring):
  65. try:
  66. path = kid.path.find(thing, from_file)
  67. except Exception:
  68. path = None
  69. if not path:
  70. if arg:
  71. thing = arg
  72. raise TemplateNotFound('Template file %r not found' % thing)
  73. try:
  74. mod = kid.load_template(path)
  75. except Exception:
  76. mod = None
  77. if not mod:
  78. raise TemplateNotFound('Could not open %r' % path)
  79. try:
  80. cls = mod.Template
  81. except AttributeError:
  82. cls = None
  83. if (isinstance(cls, TypeType)
  84. and issubclass(cls, kid.BaseTemplate)
  85. and cls != kid.Template):
  86. return cls
  87. raise TemplateNotFound('%r does not contain a template class' % path)
  88. thing = repr(thing)
  89. if arg:
  90. thing = '%s (%s)' % (arg, thing)
  91. raise TemplateNotFound('%s is not a Template class' % thing)
  92. def base_class(arg, globals, locals):
  93. """Get base class for argument with graceful exception handling."""
  94. try:
  95. from_file = globals['__file__']
  96. thing = eval(arg, globals, locals)
  97. return get_base_class(thing, from_file, arg)
  98. except Exception, e:
  99. errors = [str(e)]
  100. # try again without evaluating the argument (forgotten quotes etc.)
  101. try:
  102. return get_base_class(arg, from_file, arg)
  103. except Exception, e:
  104. errors.append(str(e))
  105. # reraise the original problem when we tried to evaluate the thing
  106. errors = '\n'.join(filter(bool, errors)) or arg
  107. raise TemplateNotFound, errors
  108. def base_class_extends(extends, globals, locals, all_extends=None):
  109. """Get Template base class for 'extends'."""
  110. try:
  111. return base_class(extends, globals, locals)
  112. except Exception, e:
  113. raise TemplateExtendsError((str(e)
  114. + '\nwhile processing extends=%r'
  115. % (all_extends or extends)).lstrip())
  116. def base_class_layout(layout, globals, locals):
  117. """Get Template base class for 'layout'."""
  118. try:
  119. return base_class(layout, globals, locals)
  120. except Exception, e:
  121. raise TemplateLayoutError((str(e)
  122. + '\nwhile processing layout=%r' % layout).lstrip())
  123. def make_attrib(attrib, encoding=None):
  124. """Generate unicode strings in dictionary."""
  125. if attrib is None:
  126. return {}
  127. if encoding is None:
  128. encoding = sys.getdefaultencoding()
  129. for (k, v) in attrib.items():
  130. if v is not None:
  131. try:
  132. v = generate_attrib(v, encoding)
  133. except TemplateAttrsError:
  134. raise TemplateAttrsError('Illegal value for attribute "%s"'
  135. % k.encode('raw_unicode_escape'))
  136. if v is None:
  137. del attrib[k]
  138. else:
  139. attrib[k] = v
  140. return attrib
  141. def generate_attrib(attrib, encoding):
  142. """Generate unicode string from attribute."""
  143. if attrib is None:
  144. return None
  145. elif isinstance(attrib, basestring):
  146. return to_unicode(attrib, encoding)
  147. elif isinstance(attrib, ElementStream):
  148. text = []
  149. for ev, item in attrib:
  150. if ev == TEXT:
  151. text.append(to_unicode(item, encoding))
  152. else:
  153. raise TemplateAttrsError
  154. if text:
  155. return ''.join(text)
  156. else:
  157. return None
  158. elif hasattr(attrib, '__iter__'):
  159. # if we get any other iterable, join the strings together:
  160. text = []
  161. for item in attrib:
  162. if item is not None:
  163. item = generate_attrib(item, encoding)
  164. if item is not None:
  165. text.append(item)
  166. if text:
  167. return ''.join(text)
  168. else:
  169. return None
  170. else:
  171. return to_unicode(attrib, encoding)
  172. def generate_content(content):
  173. """Generate ElementStream from content."""
  174. if content is None:
  175. return []
  176. elif isinstance(content, basestring):
  177. return [(TEXT, content)]
  178. elif isinstance(content, (ElementStream, kid.BaseTemplate)):
  179. return content
  180. elif isinstance(content, GeneratorType):
  181. return ElementStream(content)
  182. elif hasattr(content, 'tag') and hasattr(content, 'attrib'):
  183. # if we get an Element back, make it an ElementStream
  184. return ElementStream(content)
  185. elif hasattr(content, '__iter__'):
  186. # if we get any other iterable, chain the contents together:
  187. return itertools.chain(*itertools.imap(generate_content, content))
  188. else:
  189. return [(TEXT, unicode(content))]
  190. def filter_names(names, omit_list):
  191. for ns in names.keys():
  192. if ns in omit_list:
  193. del names[ns]
  194. return names
  195. def update_dict(a, args, globals, locals):
  196. """Update dictionary a from keyword argument string args."""
  197. try:
  198. b = eval('%s' % args, globals, locals)
  199. if not isinstance(b, dict):
  200. b = dict(b)
  201. except Exception:
  202. try:
  203. b = eval('dict(%s)' % args, globals, locals)
  204. except SyntaxError:
  205. # TypeErrror could happen with Python versions < 2.3, because
  206. # building dictionaries from keyword arguments was not supported.
  207. # Kid requires a newer Python version, so we do not catch this.
  208. # SyntaxError can happen if one of the keyword arguments is
  209. # the same as a Python keyword (e.g. "class") or if it is
  210. # a qualified name containing a namespace prefixed with a colon.
  211. # In these cases we parse the keyword arguments manually:
  212. try:
  213. try:
  214. from cStringIO import StringIO
  215. except ImportError:
  216. from StringIO import StringIO
  217. from tokenize import generate_tokens
  218. from token import NAME, OP
  219. depth, types, parts = 0, [], []
  220. for token in generate_tokens(StringIO(args).readline):
  221. type_, string = token[:2]
  222. if type_ == OP:
  223. if string == '=':
  224. if depth == 0:
  225. if len(types) > 0 \
  226. and types[-1] == NAME and parts[-1]:
  227. if len(types) > 2 \
  228. and types[-2] == OP and parts[-2] == ':' \
  229. and types[-3] == NAME and parts[-3]:
  230. parts[-3:] = ["'%s'" % ''.join(parts[-3:])]
  231. else:
  232. parts[-1] = "'%s'" % parts[-1]
  233. string = ':'
  234. elif string in '([{':
  235. depth += 1
  236. elif depth > 0 and string in ')]}':
  237. depth -= 1
  238. types.append(type_)
  239. parts.append(string)
  240. b = eval('{%s}' % ''.join(parts), globals, locals)
  241. except Exception:
  242. b = None
  243. if not isinstance(b, dict):
  244. raise
  245. for k in b.keys():
  246. if b[k] is None:
  247. del b[k]
  248. if k in a:
  249. del a[k]
  250. a.update(b)
  251. return a
  252. def update_attrs(attrib, attrs, globals, locals):
  253. """Update attributes from attrs string args."""
  254. try:
  255. return update_dict(attrib, attrs, globals, locals)
  256. except Exception, e:
  257. raise TemplateAttrsError((str(e)
  258. + '\nwhile processing attrs=%r' % attrs).lstrip())
  259. def make_updated_attrib(attrib, attrs, globals, locals, encoding=None):
  260. """"Generate unicode strings in updated dictionary."""
  261. return make_attrib(update_attrs(attrib, attrs, globals, locals), encoding)