cinap_lenrekadd hg and python4bb9e2215 years agoHistory
252 lines · 7.5 KB · Python
RawDownload
"""Interface to the compiler's internal symbol tables""" import _symtablefrom _symtable import USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM, \     DEF_STAR, DEF_DOUBLESTAR, DEF_INTUPLE, DEF_FREE, \     DEF_FREE_GLOBAL, DEF_FREE_CLASS, DEF_IMPORT, DEF_BOUND, \     OPT_IMPORT_STAR, OPT_EXEC, OPT_BARE_EXEC import weakref __all__ = ["symtable", "SymbolTable", "newSymbolTable", "Class",           "Function", "Symbol"] def symtable(code, filename, compile_type):    raw = _symtable.symtable(code, filename, compile_type)    for top in raw.itervalues():        if top.name == 'top':            break    return newSymbolTable(top, filename) class SymbolTableFactory:    def __init__(self):        self.__memo = weakref.WeakValueDictionary()     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)     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() def is_free(flags):    if (flags & (USE | DEF_FREE)) \       and (flags & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL)):        return True    if flags & DEF_FREE_CLASS:        return True    return False class SymbolTable:    def __init__(self, raw_table, filename):        self._table = raw_table        self._filename = filename        self._symbols = {}     def __repr__(self):        if self.__class__ == SymbolTable:            kind = ""        else:            kind = "%s " % self.__class__.__name__         if self._table.name == "global":            return "<%sSymbolTable for module %s>" % (kind, self._filename)        else:            return "<%sSymbolTable for %s in %s>" % (kind, self._table.name,                                                     self._filename)     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: %s" % self._table.type     def get_id(self):        return self._table.id     def get_name(self):        return self._table.name     def get_lineno(self):        return self._table.lineno     def is_optimized(self):        return bool(self._table.type == _symtable.TYPE_FUNCTION                    and not self._table.optimized)     def is_nested(self):        return bool(self._table.nested)     def has_children(self):        return bool(self._table.children)     def has_exec(self):        """Return true if the scope uses exec"""        return bool(self._table.optimized & (OPT_EXEC | OPT_BARE_EXEC))     def has_import_star(self):        """Return true if the scope uses import *"""        return bool(self._table.optimized & OPT_IMPORT_STAR)     def get_identifiers(self):        return self._table.symbols.keys()     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     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]     def get_children(self):        return [newSymbolTable(st, self._filename)                for st in self._table.children] 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])])     def get_parameters(self):        if self.__params is None:            self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)        return self.__params     def get_locals(self):        if self.__locals is None:            self.__locals = self.__idents_matching(lambda x:x & DEF_BOUND)        return self.__locals     def get_globals(self):        if self.__globals is None:            glob = DEF_GLOBAL | DEF_FREE_GLOBAL            self.__globals = self.__idents_matching(lambda x:x & glob)        return self.__globals     def get_frees(self):        if self.__frees is None:            self.__frees = self.__idents_matching(is_free)        return self.__frees class Class(SymbolTable):     __methods = None     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 class Symbol:    def __init__(self, name, flags, namespaces=None):        self.__name = name        self.__flags = flags        self.__namespaces = namespaces or ()     def __repr__(self):        return "<symbol '%s'>" % self.__name     def get_name(self):        return self.__name     def is_referenced(self):        return bool(self.__flags & _symtable.USE)     def is_parameter(self):        return bool(self.__flags & DEF_PARAM)     def is_global(self):        return bool((self.__flags & DEF_GLOBAL)                    or (self.__flags & DEF_FREE_GLOBAL))     def is_vararg(self):        return bool(self.__flags & DEF_STAR)     def is_keywordarg(self):        return bool(self.__flags & DEF_DOUBLESTAR)     def is_local(self):        return bool(self.__flags & DEF_BOUND)     def is_free(self):        if (self.__flags & (USE | DEF_FREE)) \            and (self.__flags & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL)):            return True        if self.__flags & DEF_FREE_CLASS:            return True        return False     def is_imported(self):        return bool(self.__flags & DEF_IMPORT)     def is_assigned(self):        return bool(self.__flags & DEF_LOCAL)     def is_in_tuple(self):        return bool(self.__flags & DEF_INTUPLE)     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)     def get_namespaces(self):        """Return a list of namespaces bound to this name"""        return self.__namespaces     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()