Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/library/symtable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion Lib/symtable.py
Original file line number Diff line number Diff line change
@@ -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)

Expand Down Expand Up @@ -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()
Expand All @@ -141,6 +142,11 @@ def get_globals(self):
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
Expand Down Expand Up @@ -184,6 +190,9 @@ def is_parameter(self):
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)

Expand Down
17 changes: 15 additions & 2 deletions Lib/test/test_symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import sys

glob = 42
some_var = 12

class Mine:
instance_var = 24
Expand All @@ -19,10 +20,15 @@ def a_method(p1, p2):
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():
Expand All @@ -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):
Expand Down Expand Up @@ -75,12 +82,12 @@ def test_children(self):

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",))
Expand All @@ -93,6 +100,12 @@ def test_globals(self):
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())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Expose :meth:`symtable.Symbol.is_nonlocal` in the symtable module. Patch by
Pablo Galindo.
1 change: 1 addition & 0 deletions Modules/symtablemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down