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 """ Return the toplevel *SymbolTable* for the source code. 14 15 *filename* is the name of the file with the code 16 and *compile_type* is the *compile()* mode argument. 17 """ 18 top = _symtable.symtable(code, filename, compile_type) 19 return _newSymbolTable(top, filename) 20 21class SymbolTableFactory: 22 def __init__(self): 23 self.__memo = weakref.WeakValueDictionary() 24 25 def new(self, table, filename): 26 if table.type == _symtable.TYPE_FUNCTION: 27 return Function(table, filename) 28 if table.type == _symtable.TYPE_CLASS: 29 return Class(table, filename) 30 return SymbolTable(table, filename) 31 32 def __call__(self, table, filename): 33 key = table, filename 34 obj = self.__memo.get(key, None) 35 if obj is None: 36 obj = self.__memo[key] = self.new(table, filename) 37 return obj 38 39_newSymbolTable = SymbolTableFactory() 40 41 42class SymbolTable: 43 44 def __init__(self, raw_table, filename): 45 self._table = raw_table 46 self._filename = filename 47 self._symbols = {} 48 49 def __repr__(self): 50 if self.__class__ == SymbolTable: 51 kind = "" 52 else: 53 kind = "%s " % self.__class__.__name__ 54 55 if self._table.name == "top": 56 return "<{0}SymbolTable for module {1}>".format(kind, self._filename) 57 else: 58 return "<{0}SymbolTable for {1} in {2}>".format(kind, 59 self._table.name, 60 self._filename) 61 62 def get_type(self): 63 """Return the type of the symbol table. 64 65 The values retuned are 'class', 'module' and 66 'function'. 67 """ 68 if self._table.type == _symtable.TYPE_MODULE: 69 return "module" 70 if self._table.type == _symtable.TYPE_FUNCTION: 71 return "function" 72 if self._table.type == _symtable.TYPE_CLASS: 73 return "class" 74 assert self._table.type in (1, 2, 3), \ 75 "unexpected type: {0}".format(self._table.type) 76 77 def get_id(self): 78 """Return an identifier for the table. 79 """ 80 return self._table.id 81 82 def get_name(self): 83 """Return the table's name. 84 85 This corresponds to the name of the class, function 86 or 'top' if the table is for a class, function or 87 global respectively. 88 """ 89 return self._table.name 90 91 def get_lineno(self): 92 """Return the number of the first line in the 93 block for the table. 94 """ 95 return self._table.lineno 96 97 def is_optimized(self): 98 """Return *True* if the locals in the table 99 are optimizable. 100 """ 101 return bool(self._table.type == _symtable.TYPE_FUNCTION) 102 103 def is_nested(self): 104 """Return *True* if the block is a nested class 105 or function.""" 106 return bool(self._table.nested) 107 108 def has_children(self): 109 """Return *True* if the block has nested namespaces. 110 """ 111 return bool(self._table.children) 112 113 def get_identifiers(self): 114 """Return a list of names of symbols in the table. 115 """ 116 return self._table.symbols.keys() 117 118 def lookup(self, name): 119 """Lookup a *name* in the table. 120 121 Returns a *Symbol* instance. 122 """ 123 sym = self._symbols.get(name) 124 if sym is None: 125 flags = self._table.symbols[name] 126 namespaces = self.__check_children(name) 127 module_scope = (self._table.name == "top") 128 sym = self._symbols[name] = Symbol(name, flags, namespaces, 129 module_scope=module_scope) 130 return sym 131 132 def get_symbols(self): 133 """Return a list of *Symbol* instances for 134 names in the table. 135 """ 136 return [self.lookup(ident) for ident in self.get_identifiers()] 137 138 def __check_children(self, name): 139 return [_newSymbolTable(st, self._filename) 140 for st in self._table.children 141 if st.name == name] 142 143 def get_children(self): 144 """Return a list of the nested symbol tables. 145 """ 146 return [_newSymbolTable(st, self._filename) 147 for st in self._table.children] 148 149 150class Function(SymbolTable): 151 152 # Default values for instance variables 153 __params = None 154 __locals = None 155 __frees = None 156 __globals = None 157 __nonlocals = None 158 159 def __idents_matching(self, test_func): 160 return tuple(ident for ident in self.get_identifiers() 161 if test_func(self._table.symbols[ident])) 162 163 def get_parameters(self): 164 """Return a tuple of parameters to the function. 165 """ 166 if self.__params is None: 167 self.__params = self.__idents_matching(lambda x:x & DEF_PARAM) 168 return self.__params 169 170 def get_locals(self): 171 """Return a tuple of locals in the function. 172 """ 173 if self.__locals is None: 174 locs = (LOCAL, CELL) 175 test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs 176 self.__locals = self.__idents_matching(test) 177 return self.__locals 178 179 def get_globals(self): 180 """Return a tuple of globals in the function. 181 """ 182 if self.__globals is None: 183 glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) 184 test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob 185 self.__globals = self.__idents_matching(test) 186 return self.__globals 187 188 def get_nonlocals(self): 189 """Return a tuple of nonlocals in the function. 190 """ 191 if self.__nonlocals is None: 192 self.__nonlocals = self.__idents_matching(lambda x:x & DEF_NONLOCAL) 193 return self.__nonlocals 194 195 def get_frees(self): 196 """Return a tuple of free variables in the function. 197 """ 198 if self.__frees is None: 199 is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE 200 self.__frees = self.__idents_matching(is_free) 201 return self.__frees 202 203 204class Class(SymbolTable): 205 206 __methods = None 207 208 def get_methods(self): 209 """Return a tuple of methods declared in the class. 210 """ 211 if self.__methods is None: 212 d = {} 213 for st in self._table.children: 214 d[st.name] = 1 215 self.__methods = tuple(d) 216 return self.__methods 217 218 219class Symbol: 220 221 def __init__(self, name, flags, namespaces=None, *, module_scope=False): 222 self.__name = name 223 self.__flags = flags 224 self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() 225 self.__namespaces = namespaces or () 226 self.__module_scope = module_scope 227 228 def __repr__(self): 229 return "<symbol {0!r}>".format(self.__name) 230 231 def get_name(self): 232 """Return a name of a symbol. 233 """ 234 return self.__name 235 236 def is_referenced(self): 237 """Return *True* if the symbol is used in 238 its block. 239 """ 240 return bool(self.__flags & _symtable.USE) 241 242 def is_parameter(self): 243 """Return *True* if the symbol is a parameter. 244 """ 245 return bool(self.__flags & DEF_PARAM) 246 247 def is_global(self): 248 """Return *True* if the sysmbol is global. 249 """ 250 return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) 251 or (self.__module_scope and self.__flags & DEF_BOUND)) 252 253 def is_nonlocal(self): 254 """Return *True* if the symbol is nonlocal.""" 255 return bool(self.__flags & DEF_NONLOCAL) 256 257 def is_declared_global(self): 258 """Return *True* if the symbol is declared global 259 with a global statement.""" 260 return bool(self.__scope == GLOBAL_EXPLICIT) 261 262 def is_local(self): 263 """Return *True* if the symbol is local. 264 """ 265 return bool(self.__scope in (LOCAL, CELL) 266 or (self.__module_scope and self.__flags & DEF_BOUND)) 267 268 def is_annotated(self): 269 """Return *True* if the symbol is annotated. 270 """ 271 return bool(self.__flags & DEF_ANNOT) 272 273 def is_free(self): 274 """Return *True* if a referenced symbol is 275 not assigned to. 276 """ 277 return bool(self.__scope == FREE) 278 279 def is_imported(self): 280 """Return *True* if the symbol is created from 281 an import statement. 282 """ 283 return bool(self.__flags & DEF_IMPORT) 284 285 def is_assigned(self): 286 """Return *True* if a symbol is assigned to.""" 287 return bool(self.__flags & DEF_LOCAL) 288 289 def is_namespace(self): 290 """Returns *True* if name binding introduces new namespace. 291 292 If the name is used as the target of a function or class 293 statement, this will be true. 294 295 Note that a single name can be bound to multiple objects. If 296 is_namespace() is true, the name may also be bound to other 297 objects, like an int or list, that does not introduce a new 298 namespace. 299 """ 300 return bool(self.__namespaces) 301 302 def get_namespaces(self): 303 """Return a list of namespaces bound to this name""" 304 return self.__namespaces 305 306 def get_namespace(self): 307 """Return the single namespace bound to this name. 308 309 Raises ValueError if the name is bound to multiple namespaces. 310 """ 311 if len(self.__namespaces) != 1: 312 raise ValueError("name is bound to multiple namespaces") 313 return self.__namespaces[0] 314 315if __name__ == "__main__": 316 import os, sys 317 with open(sys.argv[0]) as f: 318 src = f.read() 319 mod = symtable(src, os.path.split(sys.argv[0])[1], "exec") 320 for ident in mod.get_identifiers(): 321 info = mod.lookup(ident) 322 print(info, info.is_local(), info.is_namespace()) 323