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', 'function',
66 'annotation', 'TypeVar bound', 'type alias', and 'type parameter'.
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 if self._table.type == _symtable.TYPE_ANNOTATION:
75 return "annotation"
76 if self._table.type == _symtable.TYPE_TYPE_VAR_BOUND:
77 return "TypeVar bound"
78 if self._table.type == _symtable.TYPE_TYPE_ALIAS:
79 return "type alias"
80 if self._table.type == _symtable.TYPE_TYPE_PARAM:
81 return "type parameter"
82 assert False, f"unexpected type: {self._table.type}"
83
84 def get_id(self):
85 """Return an identifier for the table.
86 """
87 return self._table.id
88
89 def get_name(self):
90 """Return the table's name.
91
92 This corresponds to the name of the class, function
93 or 'top' if the table is for a class, function or
94 global respectively.
95 """
96 return self._table.name
97
98 def get_lineno(self):
99 """Return the number of the first line in the
100 block for the table.
101 """
102 return self._table.lineno
103
104 def is_optimized(self):
105 """Return *True* if the locals in the table
106 are optimizable.
107 """
108 return bool(self._table.type == _symtable.TYPE_FUNCTION)
109
110 def is_nested(self):
111 """Return *True* if the block is a nested class
112 or function."""
113 return bool(self._table.nested)
114
115 def has_children(self):
116 """Return *True* if the block has nested namespaces.
117 """
118 return bool(self._table.children)
119
120 def get_identifiers(self):
121 """Return a view object containing the names of symbols in the table.
122 """
123 return self._table.symbols.keys()
124
125 def lookup(self, name):
126 """Lookup a *name* in the table.
127
128 Returns a *Symbol* instance.
129 """
130 sym = self._symbols.get(name)
131 if sym is None:
132 flags = self._table.symbols[name]
133 namespaces = self.__check_children(name)
134 module_scope = (self._table.name == "top")
135 sym = self._symbols[name] = Symbol(name, flags, namespaces,
136 module_scope=module_scope)
137 return sym
138
139 def get_symbols(self):
140 """Return a list of *Symbol* instances for
141 names in the table.
142 """
143 return [self.lookup(ident) for ident in self.get_identifiers()]
144
145 def __check_children(self, name):
146 return [_newSymbolTable(st, self._filename)
147 for st in self._table.children
148 if st.name == name]
149
150 def get_children(self):
151 """Return a list of the nested symbol tables.
152 """
153 return [_newSymbolTable(st, self._filename)
154 for st in self._table.children]
155
156
157 class ESC[4;38;5;81mFunction(ESC[4;38;5;149mSymbolTable):
158
159 # Default values for instance variables
160 __params = None
161 __locals = None
162 __frees = None
163 __globals = None
164 __nonlocals = None
165
166 def __idents_matching(self, test_func):
167 return tuple(ident for ident in self.get_identifiers()
168 if test_func(self._table.symbols[ident]))
169
170 def get_parameters(self):
171 """Return a tuple of parameters to the function.
172 """
173 if self.__params is None:
174 self.__params = self.__idents_matching(lambda x:x & DEF_PARAM)
175 return self.__params
176
177 def get_locals(self):
178 """Return a tuple of locals in the function.
179 """
180 if self.__locals is None:
181 locs = (LOCAL, CELL)
182 test = lambda x: ((x >> SCOPE_OFF) & SCOPE_MASK) in locs
183 self.__locals = self.__idents_matching(test)
184 return self.__locals
185
186 def get_globals(self):
187 """Return a tuple of globals in the function.
188 """
189 if self.__globals is None:
190 glob = (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
191 test = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) in glob
192 self.__globals = self.__idents_matching(test)
193 return self.__globals
194
195 def get_nonlocals(self):
196 """Return a tuple of nonlocals in the function.
197 """
198 if self.__nonlocals is None:
199 self.__nonlocals = self.__idents_matching(lambda x:x & DEF_NONLOCAL)
200 return self.__nonlocals
201
202 def get_frees(self):
203 """Return a tuple of free variables in the function.
204 """
205 if self.__frees is None:
206 is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
207 self.__frees = self.__idents_matching(is_free)
208 return self.__frees
209
210
211 class ESC[4;38;5;81mClass(ESC[4;38;5;149mSymbolTable):
212
213 __methods = None
214
215 def get_methods(self):
216 """Return a tuple of methods declared in the class.
217 """
218 if self.__methods is None:
219 d = {}
220 for st in self._table.children:
221 d[st.name] = 1
222 self.__methods = tuple(d)
223 return self.__methods
224
225
226 class ESC[4;38;5;81mSymbol:
227
228 def __init__(self, name, flags, namespaces=None, *, module_scope=False):
229 self.__name = name
230 self.__flags = flags
231 self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
232 self.__namespaces = namespaces or ()
233 self.__module_scope = module_scope
234
235 def __repr__(self):
236 return "<symbol {0!r}>".format(self.__name)
237
238 def get_name(self):
239 """Return a name of a symbol.
240 """
241 return self.__name
242
243 def is_referenced(self):
244 """Return *True* if the symbol is used in
245 its block.
246 """
247 return bool(self.__flags & _symtable.USE)
248
249 def is_parameter(self):
250 """Return *True* if the symbol is a parameter.
251 """
252 return bool(self.__flags & DEF_PARAM)
253
254 def is_global(self):
255 """Return *True* if the symbol is global.
256 """
257 return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
258 or (self.__module_scope and self.__flags & DEF_BOUND))
259
260 def is_nonlocal(self):
261 """Return *True* if the symbol is nonlocal."""
262 return bool(self.__flags & DEF_NONLOCAL)
263
264 def is_declared_global(self):
265 """Return *True* if the symbol is declared global
266 with a global statement."""
267 return bool(self.__scope == GLOBAL_EXPLICIT)
268
269 def is_local(self):
270 """Return *True* if the symbol is local.
271 """
272 return bool(self.__scope in (LOCAL, CELL)
273 or (self.__module_scope and self.__flags & DEF_BOUND))
274
275 def is_annotated(self):
276 """Return *True* if the symbol is annotated.
277 """
278 return bool(self.__flags & DEF_ANNOT)
279
280 def is_free(self):
281 """Return *True* if a referenced symbol is
282 not assigned to.
283 """
284 return bool(self.__scope == FREE)
285
286 def is_imported(self):
287 """Return *True* if the symbol is created from
288 an import statement.
289 """
290 return bool(self.__flags & DEF_IMPORT)
291
292 def is_assigned(self):
293 """Return *True* if a symbol is assigned to."""
294 return bool(self.__flags & DEF_LOCAL)
295
296 def is_namespace(self):
297 """Returns *True* if name binding introduces new namespace.
298
299 If the name is used as the target of a function or class
300 statement, this will be true.
301
302 Note that a single name can be bound to multiple objects. If
303 is_namespace() is true, the name may also be bound to other
304 objects, like an int or list, that does not introduce a new
305 namespace.
306 """
307 return bool(self.__namespaces)
308
309 def get_namespaces(self):
310 """Return a list of namespaces bound to this name"""
311 return self.__namespaces
312
313 def get_namespace(self):
314 """Return the single namespace bound to this name.
315
316 Raises ValueError if the name is bound to multiple namespaces
317 or no namespace.
318 """
319 if len(self.__namespaces) == 0:
320 raise ValueError("name is not bound to any namespaces")
321 elif len(self.__namespaces) > 1:
322 raise ValueError("name is bound to multiple namespaces")
323 else:
324 return self.__namespaces[0]
325
326 if __name__ == "__main__":
327 import os, sys
328 with open(sys.argv[0]) as f:
329 src = f.read()
330 mod = symtable(src, os.path.split(sys.argv[0])[1], "exec")
331 for ident in mod.get_identifiers():
332 info = mod.lookup(ident)
333 print(info, info.is_local(), info.is_namespace())