/Lib/symtable.py

http://unladen-swallow.googlecode.com/ · Python · 255 lines · 246 code · 3 blank · 6 comment · 6 complexity · 42d6a4a3573b657029445650b84ebf88 MD5 · raw file

  1. """Interface to the compiler's internal symbol tables"""
  2. import _symtable
  3. from _symtable import (USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM,
  4. DEF_IMPORT, DEF_BOUND, OPT_IMPORT_STAR, OPT_EXEC, OPT_BARE_EXEC,
  5. SCOPE_OFF, SCOPE_MASK, FREE, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
  6. import warnings
  7. import weakref
  8. __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
  9. def symtable(code, filename, compile_type):
  10. raw = _symtable.symtable(code, filename, compile_type)
  11. for top in raw.itervalues():
  12. if top.name == 'top':
  13. break
  14. return _newSymbolTable(top, filename)
  15. class SymbolTableFactory:
  16. def __init__(self):
  17. self.__memo = weakref.WeakValueDictionary()
  18. def new(self, table, filename):
  19. if table.type == _symtable.TYPE_FUNCTION:
  20. return Function(table, filename)
  21. if table.type == _symtable.TYPE_CLASS:
  22. return Class(table, filename)
  23. return SymbolTable(table, filename)
  24. def __call__(self, table, filename):
  25. key = table, filename
  26. obj = self.__memo.get(key, None)
  27. if obj is None:
  28. obj = self.__memo[key] = self.new(table, filename)
  29. return obj
  30. _newSymbolTable = SymbolTableFactory()
  31. class SymbolTable(object):
  32. def __init__(self, raw_table, filename):
  33. self._table = raw_table
  34. self._filename = filename
  35. self._symbols = {}
  36. def __repr__(self):
  37. if self.__class__ == SymbolTable:
  38. kind = ""
  39. else:
  40. kind = "%s " % self.__class__.__name__
  41. if self._table.name == "global":
  42. return "<{0}SymbolTable for module {1}>".format(kind, self._filename)
  43. else:
  44. return "<{0}SymbolTable for {1} in {2}>".format(kind,
  45. self._table.name,
  46. self._filename)
  47. def get_type(self):
  48. if self._table.type == _symtable.TYPE_MODULE:
  49. return "module"
  50. if self._table.type == _symtable.TYPE_FUNCTION:
  51. return "function"
  52. if self._table.type == _symtable.TYPE_CLASS:
  53. return "class"
  54. assert self._table.type in (1, 2, 3), \
  55. "unexpected type: {0}".format(self._table.type)
  56. def get_id(self):
  57. return self._table.id
  58. def get_name(self):
  59. return self._table.name
  60. def get_lineno(self):
  61. return self._table.lineno
  62. def is_optimized(self):
  63. return bool(self._table.type == _symtable.TYPE_FUNCTION
  64. and not self._table.optimized)
  65. def is_nested(self):
  66. return bool(self._table.nested)
  67. def has_children(self):
  68. return bool(self._table.children)
  69. def has_exec(self):
  70. """Return true if the scope uses exec"""
  71. return bool(self._table.optimized & (OPT_EXEC | OPT_BARE_EXEC))
  72. def has_import_star(self):
  73. """Return true if the scope uses import *"""
  74. return bool(self._table.optimized & OPT_IMPORT_STAR)
  75. def get_identifiers(self):
  76. return self._table.symbols.keys()
  77. def lookup(self, name):
  78. sym = self._symbols.get(name)
  79. if sym is None:
  80. flags = self._table.symbols[name]
  81. namespaces = self.__check_children(name)
  82. sym = self._symbols[name] = Symbol(name, flags, namespaces)
  83. return sym
  84. def get_symbols(self):
  85. return [self.lookup(ident) for ident in self.get_identifiers()]
  86. def __check_children(self, name):
  87. return [_newSymbolTable(st, self._filename)
  88. for st in self._table.children
  89. if st.name == name]
  90. def get_children(self):
  91. return [_newSymbolTable(st, self._filename)
  92. for st in self._table.children]
  93. class Function(SymbolTable):
  94. # Default values for instance variables
  95. __params = None
  96. __locals = None
  97. __frees = None
  98. __globals = None
  99. def __idents_matching(self, test_func):
  100. return tuple([ident for ident in self.get_identifiers()
  101. if test_func(self._table.symbols[ident])])
  102. def get_parameters(self):
  103. if self.__params is None:
  104. self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)
  105. return self.__params
  106. def get_locals(self):
  107. if self.__locals is None:
  108. self.__locals = self.__idents_matching(lambda x:x & DEF_BOUND)
  109. return self.__locals
  110. def get_globals(self):
  111. if self.__globals is None:
  112. glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
  113. test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob
  114. self.__globals = self.__idents_matching(test)
  115. return self.__globals
  116. def get_frees(self):
  117. if self.__frees is None:
  118. is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
  119. self.__frees = self.__idents_matching(is_free)
  120. return self.__frees
  121. class Class(SymbolTable):
  122. __methods = None
  123. def get_methods(self):
  124. if self.__methods is None:
  125. d = {}
  126. for st in self._table.children:
  127. d[st.name] = 1
  128. self.__methods = tuple(d)
  129. return self.__methods
  130. class Symbol(object):
  131. def __init__(self, name, flags, namespaces=None):
  132. self.__name = name
  133. self.__flags = flags
  134. self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
  135. self.__namespaces = namespaces or ()
  136. def __repr__(self):
  137. return "<symbol {0!r}>".format(self.__name)
  138. def get_name(self):
  139. return self.__name
  140. def is_referenced(self):
  141. return bool(self.__flags & _symtable.USE)
  142. def is_parameter(self):
  143. return bool(self.__flags & DEF_PARAM)
  144. def is_global(self):
  145. return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
  146. def is_vararg(self):
  147. warnings.warn("is_vararg() is obsolete and will be removed",
  148. DeprecationWarning, 2)
  149. return False
  150. def is_keywordarg(self):
  151. warnings.warn("is_keywordarg() is obsolete and will be removed",
  152. DeprecationWarning, 2)
  153. return False
  154. def is_declared_global(self):
  155. return bool(self.__scope == GLOBAL_EXPLICIT)
  156. def is_local(self):
  157. return bool(self.__flags & DEF_BOUND)
  158. def is_free(self):
  159. return bool(self.__scope == FREE)
  160. def is_imported(self):
  161. return bool(self.__flags & DEF_IMPORT)
  162. def is_assigned(self):
  163. return bool(self.__flags & DEF_LOCAL)
  164. def is_in_tuple(self):
  165. warnings.warn("is_in_tuple() is obsolete and will be removed",
  166. DeprecationWarning, 2)
  167. def is_namespace(self):
  168. """Returns true if name binding introduces new namespace.
  169. If the name is used as the target of a function or class
  170. statement, this will be true.
  171. Note that a single name can be bound to multiple objects. If
  172. is_namespace() is true, the name may also be bound to other
  173. objects, like an int or list, that does not introduce a new
  174. namespace.
  175. """
  176. return bool(self.__namespaces)
  177. def get_namespaces(self):
  178. """Return a list of namespaces bound to this name"""
  179. return self.__namespaces
  180. def get_namespace(self):
  181. """Returns the single namespace bound to this name.
  182. Raises ValueError if the name is bound to multiple namespaces.
  183. """
  184. if len(self.__namespaces) != 1:
  185. raise ValueError, "name is bound to multiple namespaces"
  186. return self.__namespaces[0]
  187. if __name__ == "__main__":
  188. import os, sys
  189. src = open(sys.argv[0]).read()
  190. mod = symtable(src, os.path.split(sys.argv[0])[1], "exec")
  191. for ident in mod.get_identifiers():
  192. info = mod.lookup(ident)
  193. print info, info.is_local(), info.is_namespace()