ast.py 5.5 KB

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