1 """Interface to the compiler's internal symbol tables"""
2
3 import _symtable
4 from _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
8 import weakref
9
10 __all__ = ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
11
12 def 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
21 class ESC[4;38;5;81mSymbolTableFactory:
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
42 class ESC[4;38;5;81mSymbolTable:
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 returned 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 view object containing the 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
150 class ESC[4;38;5;81mFunction(ESC[4;38;5;149mSymbolTable):
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
204 class ESC[4;38;5;81mClass(ESC[4;38;5;149mSymbolTable):
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
219 class ESC[4;38;5;81mSymbol:
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 symbol 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 or no namespace.
311 """
312 if len(self.__namespaces) == 0:
313 raise ValueError("name is not bound to any namespaces")
314 elif len(self.__namespaces) > 1:
315 raise ValueError("name is bound to multiple namespaces")
316 else:
317 return self.__namespaces[0]
318
319 if __name__ == "__main__":
320 import os, sys
321 with open(sys.argv[0]) as f:
322 src = f.read()
323 mod = symtable(src, os.path.split(sys.argv[0])[1], "exec")
324 for ident in mod.get_identifiers():
325 info = mod.lookup(ident)
326 print(info, info.is_local(), info.is_namespace())