]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLO...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 6 Nov 2024 00:18:32 +0000 (01:18 +0100)
committerGitHub <noreply@github.com>
Wed, 6 Nov 2024 00:18:32 +0000 (00:18 +0000)
gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLOBAL (GH-120143)
(cherry picked from commit 83ba8c2bba834c0b92de669cac16fcda17485e0e)

Co-authored-by: blhsing <blhsing@gmail.com>
Lib/inspect.py
Lib/test/test_inspect/test_inspect.py
Misc/NEWS.d/next/Library/2024-06-06-04-06-05.gh-issue-70764.6511hw.rst [new file with mode: 0644]

index 8df2383f60b0115bc2c64e3409ac34a4d53068fb..161928dfa9fb09c44343f44a1017997c3296e43d 100644 (file)
@@ -1609,11 +1609,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:
index e2e4f1230abd50c0ae1d5df7438f2943efe76f45..34ae951b38ad586f099ff888cb8525e74aab2b73 100644 (file)
@@ -2160,6 +2160,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 (file)
index 0000000..4cfb66a
--- /dev/null
@@ -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.