"""Interface to the compiler's internal symbol tables"""import_symtablefrom_symtableimport(USE,DEF_GLOBAL,DEF_LOCAL,DEF_PARAM,DEF_IMPORT,DEF_BOUND,OPT_IMPORT_STAR,SCOPE_OFF,SCOPE_MASK,FREE,LOCAL,GLOBAL_IMPLICIT,GLOBAL_EXPLICIT,CELL)importweakref__all__=["symtable","SymbolTable","Class","Function","Symbol"]defsymtable(code,filename,compile_type):raw=_symtable.symtable(code,filename,compile_type)fortopinraw.values():iftop.name=='top':breakreturn_newSymbolTable(top,filename)classSymbolTableFactory:def__init__(self):self.__memo=weakref.WeakValueDictionary()defnew(self,table,filename):iftable.type==_symtable.TYPE_FUNCTION:returnFunction(table,filename)iftable.type==_symtable.TYPE_CLASS:returnClass(table,filename)returnSymbolTable(table,filename)def__call__(self,table,filename):key=table,filenameobj=self.__memo.get(key,None)ifobjisNone:obj=self.__memo[key]=self.new(table,filename)returnobj_newSymbolTable=SymbolTableFactory()classSymbolTable(object):def__init__(self,raw_table,filename):self._table=raw_tableself._filename=filenameself._symbols={}def__repr__(self):ifself.__class__==SymbolTable:kind=""else:kind="%s "%self.__class__.__name__ifself._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)defget_type(self):ifself._table.type==_symtable.TYPE_MODULE:return"module"ifself._table.type==_symtable.TYPE_FUNCTION:return"function"ifself._table.type==_symtable.TYPE_CLASS:return"class"assertself._table.typein(1,2,3), \
"unexpected type: {0}".format(self._table.type)defget_id(self):returnself._table.iddefget_name(self):returnself._table.namedefget_lineno(self):returnself._table.linenodefis_optimized(self):returnbool(self._table.type==_symtable.TYPE_FUNCTIONandnotself._table.optimized)defis_nested(self):returnbool(self._table.nested)defhas_children(self):returnbool(self._table.children)defhas_exec(self):"""Return true if the scope uses exec. Deprecated method."""returnFalsedefhas_import_star(self):"""Return true if the scope uses import *"""returnbool(self._table.optimized&OPT_IMPORT_STAR)defget_identifiers(self):returnself._table.symbols.keys()deflookup(self,name):sym=self._symbols.get(name)ifsymisNone:flags=self._table.symbols[name]namespaces=self.__check_children(name)sym=self._symbols[name]=Symbol(name,flags,namespaces)returnsymdefget_symbols(self):return[self.lookup(ident)foridentinself.get_identifiers()]def__check_children(self,name):return[_newSymbolTable(st,self._filename)forstinself._table.childrenifst.name==name]defget_children(self):return[_newSymbolTable(st,self._filename)forstinself._table.children]classFunction(SymbolTable):# Default values for instance variables__params=None__locals=None__frees=None__globals=Nonedef__idents_matching(self,test_func):returntuple([identforidentinself.get_identifiers()iftest_func(self._table.symbols[ident])])defget_parameters(self):ifself.__paramsisNone:self.__params=self.__idents_matching(lambdax:x&DEF_PARAM)returnself.__paramsdefget_locals(self):ifself.__localsisNone:locs=(LOCAL,CELL)test=lambdax:((x>>SCOPE_OFF)&SCOPE_MASK)inlocsself.__locals=self.__idents_matching(test)returnself.__localsdefget_globals(self):ifself.__globalsisNone:glob=(GLOBAL_IMPLICIT,GLOBAL_EXPLICIT)test=lambdax:((x>>SCOPE_OFF)&SCOPE_MASK)inglobself.__globals=self.__idents_matching(test)returnself.__globalsdefget_frees(self):ifself.__freesisNone:is_free=lambdax:((x>>SCOPE_OFF)&SCOPE_MASK)==FREEself.__frees=self.__idents_matching(is_free)returnself.__freesclassClass(SymbolTable):__methods=Nonedefget_methods(self):ifself.__methodsisNone:d={}forstinself._table.children:d[st.name]=1self.__methods=tuple(d)returnself.__methodsclassSymbol(object):def__init__(self,name,flags,namespaces=None):self.__name=nameself.__flags=flagsself.__scope=(flags>>SCOPE_OFF)&SCOPE_MASK# like PyST_GetScope()self.__namespaces=namespacesor()def__repr__(self):return"<symbol {0!r}>".format(self.__name)defget_name(self):returnself.__namedefis_referenced(self):returnbool(self.__flags&_symtable.USE)defis_parameter(self):returnbool(self.__flags&DEF_PARAM)defis_global(self):returnbool(self.__scopein(GLOBAL_IMPLICIT,GLOBAL_EXPLICIT))defis_declared_global(self):returnbool(self.__scope==GLOBAL_EXPLICIT)defis_local(self):returnbool(self.__flags&DEF_BOUND)defis_free(self):returnbool(self.__scope==FREE)defis_imported(self):returnbool(self.__flags&DEF_IMPORT)defis_assigned(self):returnbool(self.__flags&DEF_LOCAL)defis_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. """returnbool(self.__namespaces)defget_namespaces(self):"""Return a list of namespaces bound to this name"""returnself.__namespacesdefget_namespace(self):"""Returns the single namespace bound to this name. Raises ValueError if the name is bound to multiple namespaces. """iflen(self.__namespaces)!=1:raiseValueError("name is bound to multiple namespaces")returnself.__namespaces[0]if__name__=="__main__":importos,syswithopen(sys.argv[0])asf:src=f.read()mod=symtable(src,os.path.split(sys.argv[0])[1],"exec")foridentinmod.get_identifiers():info=mod.lookup(ident)print(info,info.is_local(),info.is_namespace())