From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:12:08 +0000 (+0100) Subject: [3.12] gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLO... X-Git-Tag: v3.12.8~106 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fc01844019f63748e8334adca57a11577cc00a6d;p=thirdparty%2FPython%2Fcpython.git [3.12] gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLOBAL (GH-120143) (#126460) gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLOBAL (GH-120143) (cherry picked from commit 83ba8c2bba834c0b92de669cac16fcda17485e0e) Co-authored-by: blhsing --- diff --git a/Lib/inspect.py b/Lib/inspect.py index c43faa73159a..b630cb283595 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1638,11 +1638,15 @@ def getclosurevars(func): global_vars = {} builtin_vars = {} unbound_names = set() - for name in code.co_names: - if name in ("None", "True", "False"): - # Because these used to be builtins instead of keywords, they - # may still show up as name references. We ignore them. - continue + global_names = set() + for instruction in dis.get_instructions(code): + opname = instruction.opname + name = instruction.argval + if opname == "LOAD_ATTR": + unbound_names.add(name) + elif opname == "LOAD_GLOBAL": + global_names.add(name) + for name in global_names: try: global_vars[name] = global_ns[name] except KeyError: diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 36978e8217cf..7d3153db1070 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -1780,6 +1780,19 @@ class TestGetClosureVars(unittest.TestCase): builtin_vars, unbound_names) self.assertEqual(inspect.getclosurevars(C().f(_arg)), expected) + def test_attribute_same_name_as_global_var(self): + class C: + _global_ref = object() + def f(): + print(C._global_ref, _global_ref) + nonlocal_vars = {"C": C} + global_vars = {"_global_ref": _global_ref} + builtin_vars = {"print": print} + unbound_names = {"_global_ref"} + expected = inspect.ClosureVars(nonlocal_vars, global_vars, + builtin_vars, unbound_names) + self.assertEqual(inspect.getclosurevars(f), expected) + def test_nonlocal_vars(self): # More complex tests of nonlocal resolution def _nonlocal_vars(f): diff --git a/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst b/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst new file mode 100644 index 000000000000..4cfb66a6ccc6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst @@ -0,0 +1 @@ +Fixed an issue where :func:`inspect.getclosurevars` would incorrectly classify an attribute name as a global variable when the name exists both as an attribute name and a global variable.