ast.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. # mako/ast.py
  2. # Copyright (C) 2006-2016 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, compat
  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, compat.string_types):
  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, compat.string_types):
  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.kwargnames[-1], **exception_kwargs)
  100. def get_argument_expressions(self, as_call=False):
  101. """Return the argument declarations of this FunctionDecl as a printable
  102. list.
  103. By default the return value is appropriate for writing in a ``def``;
  104. set `as_call` to true to build arguments to be passed to the function
  105. instead (assuming locals with the same names as the arguments exist).
  106. """
  107. namedecls = []
  108. # Build in reverse order, since defaults and slurpy args come last
  109. argnames = self.argnames[::-1]
  110. kwargnames = self.kwargnames[::-1]
  111. defaults = self.defaults[::-1]
  112. kwdefaults = self.kwdefaults[::-1]
  113. # Named arguments
  114. if self.kwargs:
  115. namedecls.append("**" + kwargnames.pop(0))
  116. for name in kwargnames:
  117. # Keyword-only arguments must always be used by name, so even if
  118. # this is a call, print out `foo=foo`
  119. if as_call:
  120. namedecls.append("%s=%s" % (name, name))
  121. elif kwdefaults:
  122. default = kwdefaults.pop(0)
  123. if default is None:
  124. # The AST always gives kwargs a default, since you can do
  125. # `def foo(*, a=1, b, c=3)`
  126. namedecls.append(name)
  127. else:
  128. namedecls.append("%s=%s" % (
  129. name, pyparser.ExpressionGenerator(default).value()))
  130. else:
  131. namedecls.append(name)
  132. # Positional arguments
  133. if self.varargs:
  134. namedecls.append("*" + argnames.pop(0))
  135. for name in argnames:
  136. if as_call or not defaults:
  137. namedecls.append(name)
  138. else:
  139. default = defaults.pop(0)
  140. namedecls.append("%s=%s" % (
  141. name, pyparser.ExpressionGenerator(default).value()))
  142. namedecls.reverse()
  143. return namedecls
  144. @property
  145. def allargnames(self):
  146. return tuple(self.argnames) + tuple(self.kwargnames)
  147. class FunctionArgs(FunctionDecl):
  148. """the argument portion of a function declaration"""
  149. def __init__(self, code, **kwargs):
  150. super(FunctionArgs, self).__init__("def ANON(%s):pass" % code,
  151. **kwargs)