ast.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # mako/ast.py
  2. # Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file>
  3. #
  4. # This module is part of Mako and is released under
  5. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  6. """utilities for analyzing expressions and blocks of Python
  7. code, as well as generating Python from AST nodes"""
  8. from mako import exceptions, pyparser, util
  9. import re
  10. class PythonCode(object):
  11. """represents information about a string containing Python code"""
  12. def __init__(self, code, **exception_kwargs):
  13. self.code = code
  14. # represents all identifiers which are assigned to at some point in
  15. # the code
  16. self.declared_identifiers = set()
  17. # represents all identifiers which are referenced before their
  18. # assignment, if any
  19. self.undeclared_identifiers = set()
  20. # note that an identifier can be in both the undeclared and declared
  21. # lists.
  22. # using AST to parse instead of using code.co_varnames,
  23. # code.co_names has several advantages:
  24. # - we can locate an identifier as "undeclared" even if
  25. # its declared later in the same block of code
  26. # - AST is less likely to break with version changes
  27. # (for example, the behavior of co_names changed a little bit
  28. # in python version 2.5)
  29. if isinstance(code, basestring):
  30. expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
  31. else:
  32. expr = code
  33. f = pyparser.FindIdentifiers(self, **exception_kwargs)
  34. f.visit(expr)
  35. class ArgumentList(object):
  36. """parses a fragment of code as a comma-separated list of expressions"""
  37. def __init__(self, code, **exception_kwargs):
  38. self.codeargs = []
  39. self.args = []
  40. self.declared_identifiers = set()
  41. self.undeclared_identifiers = set()
  42. if isinstance(code, basestring):
  43. if re.match(r"\S", code) and not re.match(r",\s*$", code):
  44. # if theres text and no trailing comma, insure its parsed
  45. # as a tuple by adding a trailing comma
  46. code += ","
  47. expr = pyparser.parse(code, "exec", **exception_kwargs)
  48. else:
  49. expr = code
  50. f = pyparser.FindTuple(self, PythonCode, **exception_kwargs)
  51. f.visit(expr)
  52. class PythonFragment(PythonCode):
  53. """extends PythonCode to provide identifier lookups in partial control
  54. statements
  55. e.g.
  56. for x in 5:
  57. elif y==9:
  58. except (MyException, e):
  59. etc.
  60. """
  61. def __init__(self, code, **exception_kwargs):
  62. m = re.match(r'^(\w+)(?:\s+(.*?))?:\s*(#|$)', code.strip(), re.S)
  63. if not m:
  64. raise exceptions.CompileException(
  65. "Fragment '%s' is not a partial control statement" %
  66. code, **exception_kwargs)
  67. if m.group(3):
  68. code = code[:m.start(3)]
  69. (keyword, expr) = m.group(1,2)
  70. if keyword in ['for','if', 'while']:
  71. code = code + "pass"
  72. elif keyword == 'try':
  73. code = code + "pass\nexcept:pass"
  74. elif keyword == 'elif' or keyword == 'else':
  75. code = "if False:pass\n" + code + "pass"
  76. elif keyword == 'except':
  77. code = "try:pass\n" + code + "pass"
  78. elif keyword == 'with':
  79. code = code + "pass"
  80. else:
  81. raise exceptions.CompileException(
  82. "Unsupported control keyword: '%s'" %
  83. keyword, **exception_kwargs)
  84. super(PythonFragment, self).__init__(code, **exception_kwargs)
  85. class FunctionDecl(object):
  86. """function declaration"""
  87. def __init__(self, code, allow_kwargs=True, **exception_kwargs):
  88. self.code = code
  89. expr = pyparser.parse(code, "exec", **exception_kwargs)
  90. f = pyparser.ParseFunc(self, **exception_kwargs)
  91. f.visit(expr)
  92. if not hasattr(self, 'funcname'):
  93. raise exceptions.CompileException(
  94. "Code '%s' is not a function declaration" % code,
  95. **exception_kwargs)
  96. if not allow_kwargs and self.kwargs:
  97. raise exceptions.CompileException(
  98. "'**%s' keyword argument not allowed here" %
  99. self.argnames[-1], **exception_kwargs)
  100. def get_argument_expressions(self, include_defaults=True):
  101. """return the argument declarations of this FunctionDecl as a printable
  102. list."""
  103. namedecls = []
  104. defaults = [d for d in self.defaults]
  105. kwargs = self.kwargs
  106. varargs = self.varargs
  107. argnames = [f for f in self.argnames]
  108. argnames.reverse()
  109. for arg in argnames:
  110. default = None
  111. if kwargs:
  112. arg = "**" + arg
  113. kwargs = False
  114. elif varargs:
  115. arg = "*" + arg
  116. varargs = False
  117. else:
  118. default = len(defaults) and defaults.pop() or None
  119. if include_defaults and default:
  120. namedecls.insert(0, "%s=%s" %
  121. (arg,
  122. pyparser.ExpressionGenerator(default).value()
  123. )
  124. )
  125. else:
  126. namedecls.insert(0, arg)
  127. return namedecls
  128. class FunctionArgs(FunctionDecl):
  129. """the argument portion of a function declaration"""
  130. def __init__(self, code, **kwargs):
  131. super(FunctionArgs, self).__init__("def ANON(%s):pass" % code,
  132. **kwargs)