Source code for symtable

"""Interface to the compiler's internal symbol tables"""

import _symtable
from _symtable import (USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM,
     DEF_IMPORT, DEF_BOUND, OPT_IMPORT_STAR, OPT_EXEC, OPT_BARE_EXEC,
     SCOPE_OFF, SCOPE_MASK, FREE, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL, LOCAL)

import weakref

__all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]

[docs]def symtable(code, filename, compile_type): top = _symtable.symtable(code, filename, compile_type) return _newSymbolTable(top, filename)
[docs]class SymbolTableFactory:
[docs] def __init__(self): self.__memo = weakref.WeakValueDictionary()
[docs] def new(self, table, filename): if table.type == _symtable.TYPE_FUNCTION: return Function(table, filename) if table.type == _symtable.TYPE_CLASS: return Class(table, filename) return SymbolTable(table, filename)
[docs] def __call__(self, table, filename): key = table, filename obj = self.__memo.get(key, None) if obj is None: obj = self.__memo[key] = self.new(table, filename) return obj
_newSymbolTable = SymbolTableFactory()
[docs]class SymbolTable(object):
[docs] def __init__(self, raw_table, filename): self._table = raw_table self._filename = filename self._symbols = {}
[docs] def __repr__(self): if self.__class__ == SymbolTable: kind = "" else: kind = "%s " % self.__class__.__name__ if self._table.name == "global": return "<{0}SymbolTable for module {1}>".format(kind, self._filename) else: return "<{0}SymbolTable for {1} in {2}>".format(kind, self._table.name, self._filename)
[docs] def get_type(self): if self._table.type == _symtable.TYPE_MODULE: return "module" if self._table.type == _symtable.TYPE_FUNCTION: return "function" if self._table.type == _symtable.TYPE_CLASS: return "class" assert self._table.type in (1, 2, 3), \ "unexpected type: {0}".format(self._table.type)
[docs] def get_id(self): return self._table.id
[docs] def get_name(self): return self._table.name
[docs] def get_lineno(self): return self._table.lineno
[docs] def is_optimized(self): return bool(self._table.type == _symtable.TYPE_FUNCTION and not self._table.optimized)
[docs] def is_nested(self): return bool(self._table.nested)
[docs] def has_children(self): return bool(self._table.children)
[docs] def has_exec(self): """Return true if the scope uses exec""" return bool(self._table.optimized & (OPT_EXEC | OPT_BARE_EXEC))
[docs] def has_import_star(self): """Return true if the scope uses import *""" return bool(self._table.optimized & OPT_IMPORT_STAR)
[docs] def get_identifiers(self): return self._table.symbols.keys()
[docs] def lookup(self, name): sym = self._symbols.get(name) if sym is None: flags = self._table.symbols[name] namespaces = self.__check_children(name) sym = self._symbols[name] = Symbol(name, flags, namespaces) return sym
[docs] def get_symbols(self): return [self.lookup(ident) for ident in self.get_identifiers()]
def __check_children(self, name): return [_newSymbolTable(st, self._filename) for st in self._table.children if st.name == name]
[docs] def get_children(self): return [_newSymbolTable(st, self._filename) for st in self._table.children]
[docs]class Function(SymbolTable): # Default values for instance variables __params = None __locals = None __frees = None __globals = None def __idents_matching(self, test_func): return tuple([ident for ident in self.get_identifiers() if test_func(self._table.symbols[ident])])
[docs] def get_parameters(self): if self.__params is None: self.__params = self.__idents_matching(lambda x:x & DEF_PARAM) return self.__params
[docs] def get_locals(self): if self.__locals is None: locs = (LOCAL, CELL) test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs self.__locals = self.__idents_matching(test) return self.__locals
[docs] def get_globals(self): if self.__globals is None: glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT) test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob self.__globals = self.__idents_matching(test) return self.__globals
[docs] def get_frees(self): if self.__frees is None: is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE self.__frees = self.__idents_matching(is_free) return self.__frees
[docs]class Class(SymbolTable): __methods = None
[docs] def get_methods(self): if self.__methods is None: d = {} for st in self._table.children: d[st.name] = 1 self.__methods = tuple(d) return self.__methods
[docs]class Symbol(object):
[docs] def __init__(self, name, flags, namespaces=None): self.__name = name self.__flags = flags self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope() self.__namespaces = namespaces or ()
[docs] def __repr__(self): return "<symbol {0!r}>".format(self.__name)
[docs] def get_name(self): return self.__name
[docs] def is_referenced(self): return bool(self.__flags & _symtable.USE)
[docs] def is_parameter(self): return bool(self.__flags & DEF_PARAM)
[docs] def is_global(self): return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
[docs] def is_declared_global(self): return bool(self.__scope == GLOBAL_EXPLICIT)
[docs] def is_local(self): return bool(self.__flags & DEF_BOUND)
[docs] def is_free(self): return bool(self.__scope == FREE)
[docs] def is_imported(self): return bool(self.__flags & DEF_IMPORT)
[docs] def is_assigned(self): return bool(self.__flags & DEF_LOCAL)
[docs] def is_namespace(self): """Returns true if name binding introduces new namespace. If the name is used as the target of a function or class statement, this will be true. Note that a single name can be bound to multiple objects. If is_namespace() is true, the name may also be bound to other objects, like an int or list, that does not introduce a new namespace. """ return bool(self.__namespaces)
[docs] def get_namespaces(self): """Return a list of namespaces bound to this name""" return self.__namespaces
[docs] def get_namespace(self): """Returns the single namespace bound to this name. Raises ValueError if the name is bound to multiple namespaces. """ if len(self.__namespaces) != 1: raise ValueError, "name is bound to multiple namespaces" return self.__namespaces[0]
if __name__ == "__main__": import os, sys src = open(sys.argv[0]).read() mod = symtable(src, os.path.split(sys.argv[0])[1], "exec") for ident in mod.get_identifiers(): info = mod.lookup(ident) print info, info.is_local(), info.is_namespace()