]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-70764: inspect.getclosurevars now identifies global variables with LOAD_GLOBAL...
authorblhsing <blhsing@gmail.com>
Tue, 5 Nov 2024 23:53:54 +0000 (07:53 +0800)
committerGitHub <noreply@github.com>
Tue, 5 Nov 2024 23:53:54 +0000 (15:53 -0800)
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 08718d82e915828ad995cb68dc4f6cda0a45ee50..e3f74e9f047eafc81fe26fa4b616133f68af4d3a 100644 (file)
@@ -1507,11 +1507,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 a4430a868676e22805dfd701782edbdf7206e424..a92627a4d60f6878571516d54889b9af4e1e5543 100644 (file)
@@ -1960,6 +1960,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.