]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] 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:12:08 +0000 (01:12 +0100)
committerGitHub <noreply@github.com>
Wed, 6 Nov 2024 00:12:08 +0000 (00:12 +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 c43faa73159a1cc25dfd7f54c18b0d4f26fb1802..b630cb2835957b5a8bbdcac1f1b94aa9e379701f 100644 (file)
@@ -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:
index 36978e8217cf9bad01415c99d56b2fdef794148c..7d3153db1070ecff00b41621829db8a8b6ce8e28 100644 (file)
@@ -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 (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.