mirror of
https://github.com/python/cpython.git
synced 2025-07-14 06:45:17 +00:00
[3.9] bpo-41840: Report module-level globals as both local and global in the symtable module (GH-22391). (GH-22528)
(cherry picked from commit fb0a4651f1
)
Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
This commit is contained in:
parent
e89253756c
commit
7b7aa94e51
3 changed files with 35 additions and 9 deletions
|
@ -34,7 +34,7 @@ class SymbolTableFactory:
|
||||||
_newSymbolTable = SymbolTableFactory()
|
_newSymbolTable = SymbolTableFactory()
|
||||||
|
|
||||||
|
|
||||||
class SymbolTable(object):
|
class SymbolTable:
|
||||||
|
|
||||||
def __init__(self, raw_table, filename):
|
def __init__(self, raw_table, filename):
|
||||||
self._table = raw_table
|
self._table = raw_table
|
||||||
|
@ -47,7 +47,7 @@ class SymbolTable(object):
|
||||||
else:
|
else:
|
||||||
kind = "%s " % self.__class__.__name__
|
kind = "%s " % self.__class__.__name__
|
||||||
|
|
||||||
if self._table.name == "global":
|
if self._table.name == "top":
|
||||||
return "<{0}SymbolTable for module {1}>".format(kind, self._filename)
|
return "<{0}SymbolTable for module {1}>".format(kind, self._filename)
|
||||||
else:
|
else:
|
||||||
return "<{0}SymbolTable for {1} in {2}>".format(kind,
|
return "<{0}SymbolTable for {1} in {2}>".format(kind,
|
||||||
|
@ -90,7 +90,9 @@ class SymbolTable(object):
|
||||||
if sym is None:
|
if sym is None:
|
||||||
flags = self._table.symbols[name]
|
flags = self._table.symbols[name]
|
||||||
namespaces = self.__check_children(name)
|
namespaces = self.__check_children(name)
|
||||||
sym = self._symbols[name] = Symbol(name, flags, namespaces)
|
module_scope = (self._table.name == "top")
|
||||||
|
sym = self._symbols[name] = Symbol(name, flags, namespaces,
|
||||||
|
module_scope=module_scope)
|
||||||
return sym
|
return sym
|
||||||
|
|
||||||
def get_symbols(self):
|
def get_symbols(self):
|
||||||
|
@ -163,13 +165,14 @@ class Class(SymbolTable):
|
||||||
return self.__methods
|
return self.__methods
|
||||||
|
|
||||||
|
|
||||||
class Symbol(object):
|
class Symbol:
|
||||||
|
|
||||||
def __init__(self, name, flags, namespaces=None):
|
def __init__(self, name, flags, namespaces=None, *, module_scope=False):
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self.__flags = flags
|
self.__flags = flags
|
||||||
self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
|
self.__scope = (flags >> SCOPE_OFF) & SCOPE_MASK # like PyST_GetScope()
|
||||||
self.__namespaces = namespaces or ()
|
self.__namespaces = namespaces or ()
|
||||||
|
self.__module_scope = module_scope
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<symbol {0!r}>".format(self.__name)
|
return "<symbol {0!r}>".format(self.__name)
|
||||||
|
@ -184,7 +187,10 @@ class Symbol(object):
|
||||||
return bool(self.__flags & DEF_PARAM)
|
return bool(self.__flags & DEF_PARAM)
|
||||||
|
|
||||||
def is_global(self):
|
def is_global(self):
|
||||||
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT))
|
"""Return *True* if the sysmbol is global.
|
||||||
|
"""
|
||||||
|
return bool(self.__scope in (GLOBAL_IMPLICIT, GLOBAL_EXPLICIT)
|
||||||
|
or (self.__module_scope and self.__flags & DEF_BOUND))
|
||||||
|
|
||||||
def is_nonlocal(self):
|
def is_nonlocal(self):
|
||||||
return bool(self.__flags & DEF_NONLOCAL)
|
return bool(self.__flags & DEF_NONLOCAL)
|
||||||
|
@ -193,7 +199,10 @@ class Symbol(object):
|
||||||
return bool(self.__scope == GLOBAL_EXPLICIT)
|
return bool(self.__scope == GLOBAL_EXPLICIT)
|
||||||
|
|
||||||
def is_local(self):
|
def is_local(self):
|
||||||
return bool(self.__scope in (LOCAL, CELL))
|
"""Return *True* if the symbol is local.
|
||||||
|
"""
|
||||||
|
return bool(self.__scope in (LOCAL, CELL)
|
||||||
|
or (self.__module_scope and self.__flags & DEF_BOUND))
|
||||||
|
|
||||||
def is_annotated(self):
|
def is_annotated(self):
|
||||||
return bool(self.__flags & DEF_ANNOT)
|
return bool(self.__flags & DEF_ANNOT)
|
||||||
|
|
|
@ -11,6 +11,8 @@ import sys
|
||||||
|
|
||||||
glob = 42
|
glob = 42
|
||||||
some_var = 12
|
some_var = 12
|
||||||
|
some_non_assigned_global_var = 11
|
||||||
|
some_assigned_global_var = 11
|
||||||
|
|
||||||
class Mine:
|
class Mine:
|
||||||
instance_var = 24
|
instance_var = 24
|
||||||
|
@ -19,6 +21,8 @@ class Mine:
|
||||||
|
|
||||||
def spam(a, b, *var, **kw):
|
def spam(a, b, *var, **kw):
|
||||||
global bar
|
global bar
|
||||||
|
global some_assigned_global_var
|
||||||
|
some_assigned_global_var = 12
|
||||||
bar = 47
|
bar = 47
|
||||||
some_var = 10
|
some_var = 10
|
||||||
x = 23
|
x = 23
|
||||||
|
@ -81,14 +85,14 @@ 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(), 12)
|
self.assertEqual(self.spam.get_lineno(), 14)
|
||||||
|
|
||||||
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', 'other_internal', 'some_var', '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", "some_assigned_global_var"])
|
||||||
self.assertEqual(self.internal.get_frees(), ("x",))
|
self.assertEqual(self.internal.get_frees(), ("x",))
|
||||||
|
|
||||||
def test_globals(self):
|
def test_globals(self):
|
||||||
|
@ -99,6 +103,9 @@ 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())
|
||||||
self.assertTrue(self.spam.lookup("bar").is_global())
|
self.assertTrue(self.spam.lookup("bar").is_global())
|
||||||
|
# Module-scope globals are both global and local
|
||||||
|
self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_global())
|
||||||
|
self.assertTrue(self.top.lookup("some_assigned_global_var").is_global())
|
||||||
|
|
||||||
def test_nonlocal(self):
|
def test_nonlocal(self):
|
||||||
self.assertFalse(self.spam.lookup("some_var").is_nonlocal())
|
self.assertFalse(self.spam.lookup("some_var").is_nonlocal())
|
||||||
|
@ -109,6 +116,9 @@ class SymtableTest(unittest.TestCase):
|
||||||
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.spam.lookup("bar").is_local())
|
self.assertFalse(self.spam.lookup("bar").is_local())
|
||||||
|
# Module-scope globals are both global and local
|
||||||
|
self.assertTrue(self.top.lookup("some_non_assigned_global_var").is_local())
|
||||||
|
self.assertTrue(self.top.lookup("some_assigned_global_var").is_local())
|
||||||
|
|
||||||
def test_free(self):
|
def test_free(self):
|
||||||
self.assertTrue(self.internal.lookup("x").is_free())
|
self.assertTrue(self.internal.lookup("x").is_free())
|
||||||
|
@ -227,6 +237,10 @@ class SymtableTest(unittest.TestCase):
|
||||||
top = symtable.symtable(code, "?", "exec")
|
top = symtable.symtable(code, "?", "exec")
|
||||||
self.assertIsNotNone(find_block(top, "\u017d"))
|
self.assertIsNotNone(find_block(top, "\u017d"))
|
||||||
|
|
||||||
|
def test_symtable_repr(self):
|
||||||
|
self.assertEqual(str(self.top), "<SymbolTable for module ?>")
|
||||||
|
self.assertEqual(str(self.spam), "<Function SymbolTable for spam in ?>")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix a bug in the :mod:`symtable` module that was causing module-scope global
|
||||||
|
variables to not be reported as both local and global. Patch by Pablo
|
||||||
|
Galindo.
|
Loading…
Add table
Add a link
Reference in a new issue