bpo-34983: Expose symtable.Symbol.is_nonlocal() in the symtable module (GH-9872)
The symbol table was not exposing functionality to query the nonlocal symbols in a function or to check if a particular symbol is nonlocal.
This commit is contained in:
parent
6395844e6a
commit
d5b4f1b5a0
|
@ -105,6 +105,10 @@ Examining Symbol Tables
|
||||||
|
|
||||||
Return a tuple containing names of globals in this function.
|
Return a tuple containing names of globals in this function.
|
||||||
|
|
||||||
|
.. method:: get_nonlocals()
|
||||||
|
|
||||||
|
Return a tuple containing names of nonlocals in this function.
|
||||||
|
|
||||||
.. method:: get_frees()
|
.. method:: get_frees()
|
||||||
|
|
||||||
Return a tuple containing names of free variables in this function.
|
Return a tuple containing names of free variables in this function.
|
||||||
|
@ -144,6 +148,10 @@ Examining Symbol Tables
|
||||||
|
|
||||||
Return ``True`` if the symbol is global.
|
Return ``True`` if the symbol is global.
|
||||||
|
|
||||||
|
.. method:: is_nonlocal()
|
||||||
|
|
||||||
|
Return ``True`` if the symbol is nonlocal.
|
||||||
|
|
||||||
.. method:: is_declared_global()
|
.. method:: is_declared_global()
|
||||||
|
|
||||||
Return ``True`` if the symbol is declared global with a global statement.
|
Return ``True`` if the symbol is declared global with a global statement.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Interface to the compiler's internal symbol tables"""
|
"""Interface to the compiler's internal symbol tables"""
|
||||||
|
|
||||||
import _symtable
|
import _symtable
|
||||||
from _symtable import (USE, DEF_GLOBAL, DEF_LOCAL, DEF_PARAM,
|
from _symtable import (USE, DEF_GLOBAL, DEF_NONLOCAL, DEF_LOCAL, DEF_PARAM,
|
||||||
DEF_IMPORT, DEF_BOUND, DEF_ANNOT, SCOPE_OFF, SCOPE_MASK, FREE,
|
DEF_IMPORT, DEF_BOUND, DEF_ANNOT, SCOPE_OFF, SCOPE_MASK, FREE,
|
||||||
LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL)
|
LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL)
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ class Function(SymbolTable):
|
||||||
__locals = None
|
__locals = None
|
||||||
__frees = None
|
__frees = None
|
||||||
__globals = None
|
__globals = None
|
||||||
|
__nonlocals = None
|
||||||
|
|
||||||
def __idents_matching(self, test_func):
|
def __idents_matching(self, test_func):
|
||||||
return tuple(ident for ident in self.get_identifiers()
|
return tuple(ident for ident in self.get_identifiers()
|
||||||
|
@ -141,6 +142,11 @@ class Function(SymbolTable):
|
||||||
self.__globals = self.__idents_matching(test)
|
self.__globals = self.__idents_matching(test)
|
||||||
return self.__globals
|
return self.__globals
|
||||||
|
|
||||||
|
def get_nonlocals(self):
|
||||||
|
if self.__nonlocals is None:
|
||||||
|
self.__nonlocals = self.__idents_matching(lambda x:x & DEF_NONLOCAL)
|
||||||
|
return self.__nonlocals
|
||||||
|
|
||||||
def get_frees(self):
|
def get_frees(self):
|
||||||
if self.__frees is None:
|
if self.__frees is None:
|
||||||
is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
|
is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
|
||||||
|
@ -184,6 +190,9 @@ class Symbol(object):
|
||||||
def is_global(self):
|
def is_global(self):
|
||||||
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
|
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
|
||||||
|
|
||||||
|
def is_nonlocal(self):
|
||||||
|
return bool(self.__flags & DEF_NONLOCAL)
|
||||||
|
|
||||||
def is_declared_global(self):
|
def is_declared_global(self):
|
||||||
return bool(self.__scope == GLOBAL_EXPLICIT)
|
return bool(self.__scope == GLOBAL_EXPLICIT)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ TEST_CODE = """
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
glob = 42
|
glob = 42
|
||||||
|
some_var = 12
|
||||||
|
|
||||||
class Mine:
|
class Mine:
|
||||||
instance_var = 24
|
instance_var = 24
|
||||||
|
@ -19,10 +20,15 @@ class Mine:
|
||||||
def spam(a, b, *var, **kw):
|
def spam(a, b, *var, **kw):
|
||||||
global bar
|
global bar
|
||||||
bar = 47
|
bar = 47
|
||||||
|
some_var = 10
|
||||||
x = 23
|
x = 23
|
||||||
glob
|
glob
|
||||||
def internal():
|
def internal():
|
||||||
return x
|
return x
|
||||||
|
def other_internal():
|
||||||
|
nonlocal some_var
|
||||||
|
some_var = 3
|
||||||
|
return some_var
|
||||||
return internal
|
return internal
|
||||||
|
|
||||||
def foo():
|
def foo():
|
||||||
|
@ -47,6 +53,7 @@ class SymtableTest(unittest.TestCase):
|
||||||
a_method = find_block(Mine, "a_method")
|
a_method = find_block(Mine, "a_method")
|
||||||
spam = find_block(top, "spam")
|
spam = find_block(top, "spam")
|
||||||
internal = find_block(spam, "internal")
|
internal = find_block(spam, "internal")
|
||||||
|
other_internal = find_block(spam, "other_internal")
|
||||||
foo = find_block(top, "foo")
|
foo = find_block(top, "foo")
|
||||||
|
|
||||||
def test_type(self):
|
def test_type(self):
|
||||||
|
@ -75,12 +82,12 @@ class SymtableTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_lineno(self):
|
def test_lineno(self):
|
||||||
self.assertEqual(self.top.get_lineno(), 0)
|
self.assertEqual(self.top.get_lineno(), 0)
|
||||||
self.assertEqual(self.spam.get_lineno(), 11)
|
self.assertEqual(self.spam.get_lineno(), 12)
|
||||||
|
|
||||||
def test_function_info(self):
|
def test_function_info(self):
|
||||||
func = self.spam
|
func = self.spam
|
||||||
self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"])
|
self.assertEqual(sorted(func.get_parameters()), ["a", "b", "kw", "var"])
|
||||||
expected = ["a", "b", "internal", "kw", "var", "x"]
|
expected = ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x']
|
||||||
self.assertEqual(sorted(func.get_locals()), expected)
|
self.assertEqual(sorted(func.get_locals()), expected)
|
||||||
self.assertEqual(sorted(func.get_globals()), ["bar", "glob"])
|
self.assertEqual(sorted(func.get_globals()), ["bar", "glob"])
|
||||||
self.assertEqual(self.internal.get_frees(), ("x",))
|
self.assertEqual(self.internal.get_frees(), ("x",))
|
||||||
|
@ -93,6 +100,12 @@ class SymtableTest(unittest.TestCase):
|
||||||
self.assertFalse(self.internal.lookup("x").is_global())
|
self.assertFalse(self.internal.lookup("x").is_global())
|
||||||
self.assertFalse(self.Mine.lookup("instance_var").is_global())
|
self.assertFalse(self.Mine.lookup("instance_var").is_global())
|
||||||
|
|
||||||
|
def test_nonlocal(self):
|
||||||
|
self.assertFalse(self.spam.lookup("some_var").is_nonlocal())
|
||||||
|
self.assertTrue(self.other_internal.lookup("some_var").is_nonlocal())
|
||||||
|
expected = ("some_var",)
|
||||||
|
self.assertEqual(self.other_internal.get_nonlocals(), expected)
|
||||||
|
|
||||||
def test_local(self):
|
def test_local(self):
|
||||||
self.assertTrue(self.spam.lookup("x").is_local())
|
self.assertTrue(self.spam.lookup("x").is_local())
|
||||||
self.assertFalse(self.internal.lookup("x").is_local())
|
self.assertFalse(self.internal.lookup("x").is_local())
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Expose :meth:`symtable.Symbol.is_nonlocal` in the symtable module. Patch by
|
||||||
|
Pablo Galindo.
|
|
@ -84,6 +84,7 @@ PyInit__symtable(void)
|
||||||
return NULL;
|
return NULL;
|
||||||
PyModule_AddIntMacro(m, USE);
|
PyModule_AddIntMacro(m, USE);
|
||||||
PyModule_AddIntMacro(m, DEF_GLOBAL);
|
PyModule_AddIntMacro(m, DEF_GLOBAL);
|
||||||
|
PyModule_AddIntMacro(m, DEF_NONLOCAL);
|
||||||
PyModule_AddIntMacro(m, DEF_LOCAL);
|
PyModule_AddIntMacro(m, DEF_LOCAL);
|
||||||
PyModule_AddIntMacro(m, DEF_PARAM);
|
PyModule_AddIntMacro(m, DEF_PARAM);
|
||||||
PyModule_AddIntMacro(m, DEF_FREE);
|
PyModule_AddIntMacro(m, DEF_FREE);
|
||||||
|
|
Loading…
Reference in New Issue