1 """Interface to the compiler's internal symbol tables"""
4 from _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
, CELL
, LOCAL
)
10 __all__
= ["symtable", "SymbolTable", "Class", "Function", "Symbol"]
12 def symtable(code
, filename
, compile_type
):
13 raw
= _symtable
.symtable(code
, filename
, compile_type
)
14 for top
in raw
.itervalues():
17 return _newSymbolTable(top
, filename
)
19 class SymbolTableFactory
:
21 self
.__memo
= weakref
.WeakValueDictionary()
23 def new(self
, table
, filename
):
24 if table
.type == _symtable
.TYPE_FUNCTION
:
25 return Function(table
, filename
)
26 if table
.type == _symtable
.TYPE_CLASS
:
27 return Class(table
, filename
)
28 return SymbolTable(table
, filename
)
30 def __call__(self
, table
, filename
):
32 obj
= self
.__memo
.get(key
, None)
34 obj
= self
.__memo
[key
] = self
.new(table
, filename
)
37 _newSymbolTable
= SymbolTableFactory()
40 class SymbolTable(object):
42 def __init__(self
, raw_table
, filename
):
43 self
._table
= raw_table
44 self
._filename
= filename
48 if self
.__class
__ == SymbolTable
:
51 kind
= "%s " % self
.__class
__.__name
__
53 if self
._table
.name
== "global":
54 return "<{0}SymbolTable for module {1}>".format(kind
, self
._filename
)
56 return "<{0}SymbolTable for {1} in {2}>".format(kind
,
61 if self
._table
.type == _symtable
.TYPE_MODULE
:
63 if self
._table
.type == _symtable
.TYPE_FUNCTION
:
65 if self
._table
.type == _symtable
.TYPE_CLASS
:
67 assert self
._table
.type in (1, 2, 3), \
68 "unexpected type: {0}".format(self
._table
.type)
74 return self
._table
.name
77 return self
._table
.lineno
79 def is_optimized(self
):
80 return bool(self
._table
.type == _symtable
.TYPE_FUNCTION
81 and not self
._table
.optimized
)
84 return bool(self
._table
.nested
)
86 def has_children(self
):
87 return bool(self
._table
.children
)
90 """Return true if the scope uses exec"""
91 return bool(self
._table
.optimized
& (OPT_EXEC | OPT_BARE_EXEC
))
93 def has_import_star(self
):
94 """Return true if the scope uses import *"""
95 return bool(self
._table
.optimized
& OPT_IMPORT_STAR
)
97 def get_identifiers(self
):
98 return self
._table
.symbols
.keys()
100 def lookup(self
, name
):
101 sym
= self
._symbols
.get(name
)
103 flags
= self
._table
.symbols
[name
]
104 namespaces
= self
.__check
_children
(name
)
105 sym
= self
._symbols
[name
] = Symbol(name
, flags
, namespaces
)
108 def get_symbols(self
):
109 return [self
.lookup(ident
) for ident
in self
.get_identifiers()]
111 def __check_children(self
, name
):
112 return [_newSymbolTable(st
, self
._filename
)
113 for st
in self
._table
.children
116 def get_children(self
):
117 return [_newSymbolTable(st
, self
._filename
)
118 for st
in self
._table
.children
]
121 class Function(SymbolTable
):
123 # Default values for instance variables
129 def __idents_matching(self
, test_func
):
130 return tuple([ident
for ident
in self
.get_identifiers()
131 if test_func(self
._table
.symbols
[ident
])])
133 def get_parameters(self
):
134 if self
.__params
is None:
135 self
.__params
= self
.__idents
_matching
(lambda x
:x
& DEF_PARAM
)
138 def get_locals(self
):
139 if self
.__locals
is None:
141 test
= lambda x
: ((x
>> SCOPE_OFF
) & SCOPE_MASK
) in locs
142 self
.__locals
= self
.__idents
_matching
(test
)
145 def get_globals(self
):
146 if self
.__globals
is None:
147 glob
= (GLOBAL_IMPLICIT
, GLOBAL_EXPLICIT
)
148 test
= lambda x
:((x
>> SCOPE_OFF
) & SCOPE_MASK
) in glob
149 self
.__globals
= self
.__idents
_matching
(test
)
150 return self
.__globals
153 if self
.__frees
is None:
154 is_free
= lambda x
:((x
>> SCOPE_OFF
) & SCOPE_MASK
) == FREE
155 self
.__frees
= self
.__idents
_matching
(is_free
)
159 class Class(SymbolTable
):
163 def get_methods(self
):
164 if self
.__methods
is None:
166 for st
in self
._table
.children
:
168 self
.__methods
= tuple(d
)
169 return self
.__methods
172 class Symbol(object):
174 def __init__(self
, name
, flags
, namespaces
=None):
177 self
.__scope
= (flags
>> SCOPE_OFF
) & SCOPE_MASK
# like PyST_GetScope()
178 self
.__namespaces
= namespaces
or ()
181 return "<symbol {0!r}>".format(self
.__name
)
186 def is_referenced(self
):
187 return bool(self
.__flags
& _symtable
.USE
)
189 def is_parameter(self
):
190 return bool(self
.__flags
& DEF_PARAM
)
193 return bool(self
.__scope
in (GLOBAL_IMPLICIT
, GLOBAL_EXPLICIT
))
195 def is_declared_global(self
):
196 return bool(self
.__scope
== GLOBAL_EXPLICIT
)
199 return bool(self
.__flags
& DEF_BOUND
)
202 return bool(self
.__scope
== FREE
)
204 def is_imported(self
):
205 return bool(self
.__flags
& DEF_IMPORT
)
207 def is_assigned(self
):
208 return bool(self
.__flags
& DEF_LOCAL
)
210 def is_namespace(self
):
211 """Returns true if name binding introduces new namespace.
213 If the name is used as the target of a function or class
214 statement, this will be true.
216 Note that a single name can be bound to multiple objects. If
217 is_namespace() is true, the name may also be bound to other
218 objects, like an int or list, that does not introduce a new
221 return bool(self
.__namespaces
)
223 def get_namespaces(self
):
224 """Return a list of namespaces bound to this name"""
225 return self
.__namespaces
227 def get_namespace(self
):
228 """Returns the single namespace bound to this name.
230 Raises ValueError if the name is bound to multiple namespaces.
232 if len(self
.__namespaces
) != 1:
233 raise ValueError, "name is bound to multiple namespaces"
234 return self
.__namespaces
[0]
236 if __name__
== "__main__":
238 src
= open(sys
.argv
[0]).read()
239 mod
= symtable(src
, os
.path
.split(sys
.argv
[0])[1], "exec")
240 for ident
in mod
.get_identifiers():
241 info
= mod
.lookup(ident
)
242 print info
, info
.is_local(), info
.is_namespace()