https://github.com/python/cpython/commit/fc01844019f63748e8334adca57a11577cc00a6d
commit: fc01844019f63748e8334adca57a11577cc00a6d
branch: 3.12
author: Miss Islington (bot) <[email protected]>
committer: carljm <[email protected]>
date: 2024-11-06T00:12:08Z
summary:

[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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst
M Lib/inspect.py
M Lib/test/test_inspect/test_inspect.py

diff --git a/Lib/inspect.py b/Lib/inspect.py
index c43faa73159a1c..b630cb2835957b 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 36978e8217cf9b..7d3153db1070ec 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -1780,6 +1780,19 @@ def g(local_ref):
                                        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 00000000000000..4cfb66a6ccc6ee
--- /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.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to