PageRenderTime 33ms CodeModel.GetById 13ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 1ms

/Lib/symtable.py

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