"""Interface to the compiler's internal symbol tables"""
from _symtable
import USE
, DEF_GLOBAL
, DEF_LOCAL
, DEF_PARAM
, \
DEF_STAR
, DEF_DOUBLESTAR
, DEF_INTUPLE
, DEF_FREE
, \
DEF_FREE_GLOBAL
, DEF_FREE_CLASS
, DEF_IMPORT
, DEF_BOUND
, \
OPT_IMPORT_STAR
, OPT_EXEC
, OPT_BARE_EXEC
__all__
= ["symtable", "SymbolTable", "newSymbolTable", "Class",
def symtable(code
, filename
, compile_type
):
raw
= _symtable
.symtable(code
, filename
, compile_type
)
return newSymbolTable(raw
[0], filename
)
class SymbolTableFactory
:
self
.__memo
= weakref
.WeakValueDictionary()
def new(self
, table
, filename
):
if table
.type == _symtable
.TYPE_FUNCTION
:
return Function(table
, filename
)
if table
.type == _symtable
.TYPE_CLASS
:
return Class(table
, filename
)
return SymbolTable(table
, filename
)
def __call__(self
, table
, filename
):
obj
= self
.__memo
.get(key
, None)
obj
= self
.__memo
[key
] = self
.new(table
, filename
)
newSymbolTable
= SymbolTableFactory()
if (flags
& (USE | DEF_FREE
)) \
and (flags
& (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL
)):
if flags
& DEF_FREE_CLASS
:
def __init__(self
, raw_table
, filename
):
self
._filename
= filename
if self
.__class
__ == SymbolTable
:
kind
= "%s " % self
.__class
__.__name
__
if self
._table
.name
== "global":
return "<%sSymbolTable for module %s>" % (kind
, self
._filename
)
return "<%sSymbolTable for %s in %s>" % (kind
, self
._table
.name
,
if self
._table
.type == _symtable
.TYPE_MODULE
:
if self
._table
.type == _symtable
.TYPE_FUNCTION
:
if self
._table
.type == _symtable
.TYPE_CLASS
:
assert self
._table
.type in (1, 2, 3), \
"unexpected type: %s" % self
._table
.type
return self
._table
.lineno
return bool(self
._table
.type == _symtable
.TYPE_FUNCTION
and not self
._table
.optimized
)
return bool(self
._table
.nested
)
return bool(self
._table
.children
)
"""Return true if the scope uses exec"""
return bool(self
._table
.optimized
& (OPT_EXEC | OPT_BARE_EXEC
))
def has_import_star(self
):
"""Return true if the scope uses import *"""
return bool(self
._table
.optimized
& OPT_IMPORT_STAR
)
def get_identifiers(self
):
return self
._table
.symbols
.keys()
sym
= self
._symbols
.get(name
)
flags
= self
._table
.symbols
[name
]
namespaces
= self
.__check
_children
(name
)
sym
= self
._symbols
[name
] = Symbol(name
, flags
, namespaces
)
return [self
.lookup(ident
) for ident
in self
.get_identifiers()]
def __check_children(self
, name
):
return [newSymbolTable(st
, self
._filename
)
for st
in self
._table
.children
return [newSymbolTable(st
, self
._filename
)
for st
in self
._table
.children
]
class Function(SymbolTable
):
# Default values for instance variables
def __idents_matching(self
, test_func
):
return tuple([ident
for ident
in self
.get_identifiers()
if test_func(self
._table
.symbols
[ident
])])
def get_parameters(self
):
if self
.__params
is None:
self
.__params
= self
.__idents
_matching
(lambda x
:x
& DEF_PARAM
)
if self
.__locals
is None:
self
.__locals
= self
.__idents
_matching
(lambda x
:x
& DEF_BOUND
)
if self
.__globals
is None:
glob
= DEF_GLOBAL | DEF_FREE_GLOBAL
self
.__globals
= self
.__idents
_matching
(lambda x
:x
& glob
)
self
.__frees
= self
.__idents
_matching
(is_free
)
class Class(SymbolTable
):
if self
.__methods
is None:
for st
in self
._table
.children
:
self
.__methods
= tuple(d
)
def __init__(self
, name
, flags
, namespaces
=None):
self
.__namespaces
= namespaces
or ()
return "<symbol '%s'>" % self
.__name
return bool(self
.__flags
& _symtable
.USE
)
return bool(self
.__flags
& DEF_PARAM
)
return bool((self
.__flags
& DEF_GLOBAL
)
or (self
.__flags
& DEF_FREE_GLOBAL
))
return bool(self
.__flags
& DEF_STAR
)
return bool(self
.__flags
& DEF_DOUBLESTAR
)
return bool(self
.__flags
& DEF_BOUND
)
if (self
.__flags
& (USE | DEF_FREE
)) \
and (self
.__flags
& (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL
)):
if self
.__flags
& DEF_FREE_CLASS
:
return bool(self
.__flags
& DEF_IMPORT
)
return bool(self
.__flags
& DEF_LOCAL
)
return bool(self
.__flags
& DEF_INTUPLE
)
"""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
return bool(self
.__namespaces
)
def get_namespaces(self
):
"""Return a list of namespaces bound to this name"""
"""Returns the single namespace bound to this name.
Raises ValueError if the name is bound to multiple namespaces.
if len(self
.__namespaces
) != 1:
raise ValueError, "name is bound to multiple namespaces"
return self
.__namespaces
[0]
if __name__
== "__main__":
src
= open(sys
.argv
[0]).read()
mod
= symtable(src
, os
.path
.split(sys
.argv
[0])[1], "exec")
for ident
in mod
.get_identifiers():
print info
, info
.is_local(), info
.is_namespace()