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