summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2018-10-20 00:46:00 (GMT)
committerGitHub <noreply@github.com>2018-10-20 00:46:00 (GMT)
commitd5b4f1b5a064c0d858352100fcddb91c363afa51 (patch)
treecae99173cc824e62289c7ebd8e5be290a206d0c1
parent6395844e6adebc12c4eba1fb75c5e7c9c8b89f85 (diff)
downloadcpython-d5b4f1b5a064c0d858352100fcddb91c363afa51.zip
cpython-d5b4f1b5a064c0d858352100fcddb91c363afa51.tar.gz
cpython-d5b4f1b5a064c0d858352100fcddb91c363afa51.tar.bz2
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.
-rw-r--r--Doc/library/symtable.rst8
-rw-r--r--Lib/symtable.py11
-rw-r--r--Lib/test/test_symtable.py17
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-10-14-17-26-41.bpo-34983.l8XaZd.rst2
-rw-r--r--Modules/symtablemodule.c1
5 files changed, 36 insertions, 3 deletions
diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst
index ba2caff..7c6ac4d 100644
--- a/Doc/library/symtable.rst
+++ b/Doc/library/symtable.rst
@@ -105,6 +105,10 @@ Examining Symbol Tables
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()
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.
+ .. method:: is_nonlocal()
+
+ Return ``True`` if the symbol is nonlocal.
+
.. method:: is_declared_global()
Return ``True`` if the symbol is declared global with a global statement.
diff --git a/Lib/symtable.py b/Lib/symtable.py
index c7627a6..5bea7cf 100644
--- a/Lib/symtable.py
+++ b/Lib/symtable.py
@@ -1,7 +1,7 @@
"""Interface to the compiler's internal symbol tables"""
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,
LOCAL, GLOBAL_IMPLICIT, GLOBAL_EXPLICIT, CELL)
@@ -117,6 +117,7 @@ class Function(SymbolTable):
__locals = None
__frees = None
__globals = None
+ __nonlocals = None
def __idents_matching(self, test_func):
return tuple(ident for ident in self.get_identifiers()
@@ -141,6 +142,11 @@ class Function(SymbolTable):
self.__globals = self.__idents_matching(test)
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):
if self.__frees is None:
is_free = lambda x:((x >> SCOPE_OFF) & SCOPE_MASK) == FREE
@@ -184,6 +190,9 @@ class Symbol(object):
def is_global(self):
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
+ def is_nonlocal(self):
+ return bool(self.__flags & DEF_NONLOCAL)
+
def is_declared_global(self):
return bool(self.__scope == GLOBAL_EXPLICIT)
diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py
index 8d76f6f..0a1cb8d 100644
--- a/Lib/test/test_symtable.py
+++ b/Lib/test/test_symtable.py
@@ -10,6 +10,7 @@ TEST_CODE = """
import sys
glob = 42
+some_var = 12
class Mine:
instance_var = 24
@@ -19,10 +20,15 @@ class Mine:
def spam(a, b, *var, **kw):
global bar
bar = 47
+ some_var = 10
x = 23
glob
def internal():
return x
+ def other_internal():
+ nonlocal some_var
+ some_var = 3
+ return some_var
return internal
def foo():
@@ -47,6 +53,7 @@ class SymtableTest(unittest.TestCase):
a_method = find_block(Mine, "a_method")
spam = find_block(top, "spam")
internal = find_block(spam, "internal")
+ other_internal = find_block(spam, "other_internal")
foo = find_block(top, "foo")
def test_type(self):
@@ -75,12 +82,12 @@ class SymtableTest(unittest.TestCase):
def test_lineno(self):
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):
func = self.spam
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_globals()), ["bar", "glob"])
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.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):
self.assertTrue(self.spam.lookup("x").is_local())
self.assertFalse(self.internal.lookup("x").is_local())
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-10-14-17-26-41.bpo-34983.l8XaZd.rst b/Misc/NEWS.d/next/Core and Builtins/2018-10-14-17-26-41.bpo-34983.l8XaZd.rst
new file mode 100644
index 0000000..dd76b63
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-10-14-17-26-41.bpo-34983.l8XaZd.rst
@@ -0,0 +1,2 @@
+Expose :meth:`symtable.Symbol.is_nonlocal` in the symtable module. Patch by
+Pablo Galindo.
diff --git a/Modules/symtablemodule.c b/Modules/symtablemodule.c
index 810f88d..e8d2f5b 100644
--- a/Modules/symtablemodule.c
+++ b/Modules/symtablemodule.c
@@ -84,6 +84,7 @@ PyInit__symtable(void)
return NULL;
PyModule_AddIntMacro(m, USE);
PyModule_AddIntMacro(m, DEF_GLOBAL);
+ PyModule_AddIntMacro(m, DEF_NONLOCAL);
PyModule_AddIntMacro(m, DEF_LOCAL);
PyModule_AddIntMacro(m, DEF_PARAM);
PyModule_AddIntMacro(m, DEF_FREE);