]> granicus.if.org Git - python/commitdiff
bpo-36876: Add a tool that identifies unsupported global C variables. (#15877)
authorEric Snow <ericsnowcurrently@gmail.com>
Wed, 11 Sep 2019 18:49:45 +0000 (19:49 +0100)
committerGitHub <noreply@github.com>
Wed, 11 Sep 2019 18:49:45 +0000 (19:49 +0100)
51 files changed:
Lib/test/test_check_c_globals.py [new file with mode: 0644]
Lib/test/test_clinic.py
Lib/test/test_tools/__init__.py
Lib/test/test_tools/test_c_analyzer/__init__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/__main__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/__init__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_files.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_info.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_known.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/__init__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/test___main__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/test_find.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/test_functional.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/test_show.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_globals/test_supported.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_parser/__init__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_parser/test_declarations.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_parser/test_info.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_parser/test_preprocessor.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_symbols/__init__.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/test_c_symbols/test_info.py [new file with mode: 0644]
Lib/test/test_tools/test_c_analyzer/util.py [new file with mode: 0644]
Tools/c-analyzer/README [moved from Tools/c-globals/README with 100% similarity]
Tools/c-analyzer/c-globals.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/__init__.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/_generate.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/files.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/info.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/known.py [new file with mode: 0644]
Tools/c-analyzer/c_analyzer_common/util.py [new file with mode: 0644]
Tools/c-analyzer/c_globals/README [new file with mode: 0644]
Tools/c-analyzer/c_globals/__init__.py [new file with mode: 0644]
Tools/c-analyzer/c_globals/__main__.py [new file with mode: 0644]
Tools/c-analyzer/c_globals/find.py [new file with mode: 0644]
Tools/c-analyzer/c_globals/show.py [new file with mode: 0644]
Tools/c-analyzer/c_globals/supported.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/__init__.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/declarations.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/info.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/naive.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/preprocessor.py [new file with mode: 0644]
Tools/c-analyzer/c_parser/source.py [new file with mode: 0644]
Tools/c-analyzer/c_symbols/__init__.py [new file with mode: 0644]
Tools/c-analyzer/c_symbols/binary.py [new file with mode: 0644]
Tools/c-analyzer/c_symbols/info.py [new file with mode: 0644]
Tools/c-analyzer/c_symbols/resolve.py [new file with mode: 0644]
Tools/c-analyzer/c_symbols/source.py [new file with mode: 0644]
Tools/c-analyzer/check-c-globals.py [moved from Tools/c-globals/check-c-globals.py with 100% similarity]
Tools/c-analyzer/ignored-globals.txt [moved from Tools/c-globals/ignored-globals.txt with 100% similarity]
Tools/c-analyzer/ignored.tsv [new file with mode: 0644]
Tools/c-analyzer/known.tsv [new file with mode: 0644]

diff --git a/Lib/test/test_check_c_globals.py b/Lib/test/test_check_c_globals.py
new file mode 100644 (file)
index 0000000..009560e
--- /dev/null
@@ -0,0 +1,22 @@
+import unittest
+import test.test_tools
+
+test.test_tools.skip_if_missing('c-analyzer')
+with test.test_tools.imports_under_tool('c-analyzer'):
+    from c_globals.__main__ import main
+
+
+class ActualChecks(unittest.TestCase):
+
+    # XXX Also run the check in "make check".
+    @unittest.expectedFailure
+    def test_check_c_globals(self):
+        try:
+            main('check', {})
+        except NotImplementedError:
+            raise unittest.SkipTest('not supported on this host')
+
+
+if __name__ == '__main__':
+    # Test needs to be a package, so we can do relative imports.
+    unittest.main()
index 244c5fecd3491562e723b653e772660df62b6eff..3d5dc4759d5018ff0bd0d16afab52d09f540f497 100644 (file)
@@ -2,7 +2,7 @@
 # Copyright 2012-2013 by Larry Hastings.
 # Licensed to the PSF under a contributor agreement.
 
-from test import support
+from test import support, test_tools
 from unittest import TestCase
 import collections
 import inspect
@@ -10,17 +10,10 @@ import os.path
 import sys
 import unittest
 
-
-clinic_path = os.path.join(os.path.dirname(__file__), '..', '..', 'Tools', 'clinic')
-clinic_path = os.path.normpath(clinic_path)
-if not os.path.exists(clinic_path):
-    raise unittest.SkipTest(f'{clinic_path!r} path does not exist')
-sys.path.append(clinic_path)
-try:
+test_tools.skip_if_missing('clinic')
+with test_tools.imports_under_tool('clinic'):
     import clinic
     from clinic import DSLParser
-finally:
-    del sys.path[-1]
 
 
 class FakeConverter:
index 4d0fca330a1146243f7ed2631ca63d1a4be0e7f7..eb9acad677d58a3d03476f87a81c0e24faebddf0 100644 (file)
@@ -1,20 +1,33 @@
 """Support functions for testing scripts in the Tools directory."""
-import os
-import unittest
+import contextlib
 import importlib
+import os.path
+import unittest
 from test import support
 
-basepath = os.path.dirname(                 # <src/install dir>
-                os.path.dirname(                # Lib
-                    os.path.dirname(                # test
-                        os.path.dirname(__file__))))    # test_tools
+basepath = os.path.normpath(
+        os.path.dirname(                 # <src/install dir>
+            os.path.dirname(                # Lib
+                os.path.dirname(                # test
+                    os.path.dirname(__file__)))))    # test_tools
 
 toolsdir = os.path.join(basepath, 'Tools')
 scriptsdir = os.path.join(toolsdir, 'scripts')
 
-def skip_if_missing():
-    if not os.path.isdir(scriptsdir):
-        raise unittest.SkipTest('scripts directory could not be found')
+def skip_if_missing(tool=None):
+    if tool:
+        tooldir = os.path.join(toolsdir, tool)
+    else:
+        tool = 'scripts'
+        tooldir = scriptsdir
+    if not os.path.isdir(tooldir):
+        raise unittest.SkipTest(f'{tool} directory could not be found')
+
+@contextlib.contextmanager
+def imports_under_tool(name, *subdirs):
+    tooldir = os.path.join(toolsdir, name, *subdirs)
+    with support.DirsOnSysPath(tooldir) as cm:
+        yield cm
 
 def import_tool(toolname):
     with support.DirsOnSysPath(scriptsdir):
diff --git a/Lib/test/test_tools/test_c_analyzer/__init__.py b/Lib/test/test_tools/test_c_analyzer/__init__.py
new file mode 100644 (file)
index 0000000..d0b4c04
--- /dev/null
@@ -0,0 +1,15 @@
+import contextlib
+import os.path
+import test.test_tools
+from test.support import load_package_tests
+
+
+@contextlib.contextmanager
+def tool_imports_for_tests():
+    test.test_tools.skip_if_missing('c-analyzer')
+    with test.test_tools.imports_under_tool('c-analyzer'):
+        yield
+
+
+def load_tests(*args):
+    return load_package_tests(os.path.dirname(__file__), *args)
diff --git a/Lib/test/test_tools/test_c_analyzer/__main__.py b/Lib/test/test_tools/test_c_analyzer/__main__.py
new file mode 100644 (file)
index 0000000..b5b017d
--- /dev/null
@@ -0,0 +1,5 @@
+from . import load_tests
+import unittest
+
+
+unittest.main()
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_files.py b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_files.py
new file mode 100644 (file)
index 0000000..6d14aea
--- /dev/null
@@ -0,0 +1,470 @@
+import os.path
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common.files import (
+            iter_files, _walk_tree, glob_tree,
+            )
+
+
+def fixpath(filename):
+    return filename.replace('/', os.path.sep)
+
+
+class IterFilesTests(unittest.TestCase):
+
+    maxDiff = None
+
+    _return_walk = None
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+    def set_files(self, *filesperroot):
+        roots = []
+        result = []
+        for root, files in filesperroot:
+            root = fixpath(root)
+            roots.append(root)
+            result.append([os.path.join(root, fixpath(f))
+                           for f in files])
+        self._return_walk = result
+        return roots
+
+    def _walk(self, root, *, suffix=None, walk=None):
+        self.calls.append(('_walk', (root, suffix, walk)))
+        return iter(self._return_walk.pop(0))
+
+    def _glob(self, root, *, suffix=None):
+        self.calls.append(('_glob', (root, suffix)))
+        return iter(self._return_walk.pop(0))
+
+    def test_typical(self):
+        dirnames = self.set_files(
+            ('spam', ['file1.c', 'file2.c']),
+            ('eggs', ['ham/file3.h']),
+            )
+        suffixes = ('.c', '.h')
+
+        files = list(iter_files(dirnames, suffixes,
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            fixpath('eggs/ham/file3.h'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', None, _walk_tree)),
+            ('_walk', ('eggs', None, _walk_tree)),
+            ])
+
+    def test_single_root(self):
+        self._return_walk = [
+                [fixpath('spam/file1.c'), fixpath('spam/file2.c')],
+                ]
+
+        files = list(iter_files('spam', '.c',
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', '.c', _walk_tree)),
+            ])
+
+    def test_one_root(self):
+        self._return_walk = [
+                [fixpath('spam/file1.c'), fixpath('spam/file2.c')],
+                ]
+
+        files = list(iter_files(['spam'], '.c',
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', '.c', _walk_tree)),
+            ])
+
+    def test_multiple_roots(self):
+        dirnames = self.set_files(
+            ('spam', ['file1.c', 'file2.c']),
+            ('eggs', ['ham/file3.c']),
+            )
+
+        files = list(iter_files(dirnames, '.c',
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            fixpath('eggs/ham/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', '.c', _walk_tree)),
+            ('_walk', ('eggs', '.c', _walk_tree)),
+            ])
+
+    def test_no_roots(self):
+        files = list(iter_files([], '.c',
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [])
+        self.assertEqual(self.calls, [])
+
+    def test_single_suffix(self):
+        self._return_walk = [
+                [fixpath('spam/file1.c'),
+                 fixpath('spam/eggs/file3.c'),
+                 ],
+                ]
+
+        files = list(iter_files('spam', '.c',
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/eggs/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', '.c', _walk_tree)),
+            ])
+
+    def test_one_suffix(self):
+        self._return_walk = [
+                [fixpath('spam/file1.c'),
+                 fixpath('spam/file1.h'),
+                 fixpath('spam/file1.o'),
+                 fixpath('spam/eggs/file3.c'),
+                 ],
+                ]
+
+        files = list(iter_files('spam', ['.c'],
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/eggs/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', None, _walk_tree)),
+            ])
+
+    def test_multiple_suffixes(self):
+        self._return_walk = [
+                [fixpath('spam/file1.c'),
+                 fixpath('spam/file1.h'),
+                 fixpath('spam/file1.o'),
+                 fixpath('spam/eggs/file3.c'),
+                 ],
+                ]
+
+        files = list(iter_files('spam', ('.c', '.h'),
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file1.h'),
+            fixpath('spam/eggs/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', None, _walk_tree)),
+            ])
+
+    def test_no_suffix(self):
+        expected = [fixpath('spam/file1.c'),
+                    fixpath('spam/file1.h'),
+                    fixpath('spam/file1.o'),
+                    fixpath('spam/eggs/file3.c'),
+                    ]
+        for suffix in (None, '', ()):
+            with self.subTest(suffix):
+                self.calls.clear()
+                self._return_walk = [list(expected)]
+
+                files = list(iter_files('spam', suffix,
+                                        _glob=self._glob,
+                                        _walk=self._walk))
+
+                self.assertEqual(files, expected)
+                self.assertEqual(self.calls, [
+                    ('_walk', ('spam', suffix, _walk_tree)),
+                    ])
+
+    def test_relparent(self):
+        dirnames = self.set_files(
+            ('/x/y/z/spam', ['file1.c', 'file2.c']),
+            ('/x/y/z/eggs', ['ham/file3.c']),
+            )
+
+        files = list(iter_files(dirnames, '.c', fixpath('/x/y'),
+                                _glob=self._glob,
+                                _walk=self._walk))
+
+        self.assertEqual(files, [
+            fixpath('z/spam/file1.c'),
+            fixpath('z/spam/file2.c'),
+            fixpath('z/eggs/ham/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', (fixpath('/x/y/z/spam'), '.c', _walk_tree)),
+            ('_walk', (fixpath('/x/y/z/eggs'), '.c', _walk_tree)),
+            ])
+
+    def test_glob(self):
+        dirnames = self.set_files(
+            ('spam', ['file1.c', 'file2.c']),
+            ('eggs', ['ham/file3.c']),
+            )
+
+        files = list(iter_files(dirnames, '.c',
+                                get_files=glob_tree,
+                                _walk=self._walk,
+                                _glob=self._glob))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            fixpath('eggs/ham/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_glob', ('spam', '.c')),
+            ('_glob', ('eggs', '.c')),
+            ])
+
+
+    def test_alt_walk_func(self):
+        dirnames = self.set_files(
+            ('spam', ['file1.c', 'file2.c']),
+            ('eggs', ['ham/file3.c']),
+            )
+        def get_files(root):
+            return None
+
+        files = list(iter_files(dirnames, '.c',
+                                get_files=get_files,
+                                _walk=self._walk,
+                                _glob=self._glob))
+
+        self.assertEqual(files, [
+            fixpath('spam/file1.c'),
+            fixpath('spam/file2.c'),
+            fixpath('eggs/ham/file3.c'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_walk', ('spam', '.c', get_files)),
+            ('_walk', ('eggs', '.c', get_files)),
+            ])
+
+
+
+
+
+
+#    def test_no_dirnames(self):
+#        dirnames = []
+#        filter_by_name = None
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [])
+#        self.assertEqual(self.calls, [])
+#
+#    def test_no_filter(self):
+#        self._return_walk = [
+#                [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                ]
+#        filter_by_name = None
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [
+#            fixpath('spam/file1'),
+#            fixpath('spam/file2.c'),
+#            fixpath('spam/file3.h'),
+#            fixpath('spam/file4.o'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ])
+#
+#    def test_no_files(self):
+#        self._return_walk = [
+#                [('spam', (), ()),
+#                 ],
+#                [(fixpath('eggs/ham'), (), ()),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                fixpath('eggs/ham'),
+#                ]
+#        filter_by_name = None
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ('_walk', (fixpath('eggs/ham'),)),
+#            ])
+#
+#    def test_tree(self):
+#        self._return_walk = [
+#                [('spam', ('sub1', 'sub2', 'sub3'), ('file1',)),
+#                 (fixpath('spam/sub1'), ('sub1sub1',), ('file2', 'file3')),
+#                 (fixpath('spam/sub1/sub1sub1'), (), ('file4',)),
+#                 (fixpath('spam/sub2'), (), ()),
+#                 (fixpath('spam/sub3'), (), ('file5',)),
+#                 ],
+#                [(fixpath('eggs/ham'), (), ('file6',)),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                fixpath('eggs/ham'),
+#                ]
+#        filter_by_name = None
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [
+#            fixpath('spam/file1'),
+#            fixpath('spam/sub1/file2'),
+#            fixpath('spam/sub1/file3'),
+#            fixpath('spam/sub1/sub1sub1/file4'),
+#            fixpath('spam/sub3/file5'),
+#            fixpath('eggs/ham/file6'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ('_walk', (fixpath('eggs/ham'),)),
+#            ])
+#
+#    def test_filter_suffixes(self):
+#        self._return_walk = [
+#                [('spam', (), ('file1', 'file2.c', 'file3.h', 'file4.o')),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                ]
+#        filter_by_name = ('.c', '.h')
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [
+#            fixpath('spam/file2.c'),
+#            fixpath('spam/file3.h'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ])
+#
+#    def test_some_filtered(self):
+#        self._return_walk = [
+#                [('spam', (), ('file1', 'file2', 'file3', 'file4')),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                ]
+#        def filter_by_name(filename, results=[False, True, False, True]):
+#            self.calls.append(('filter_by_name', (filename,)))
+#            return results.pop(0)
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [
+#            fixpath('spam/file2'),
+#            fixpath('spam/file4'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ('filter_by_name', ('file1',)),
+#            ('filter_by_name', ('file2',)),
+#            ('filter_by_name', ('file3',)),
+#            ('filter_by_name', ('file4',)),
+#            ])
+#
+#    def test_none_filtered(self):
+#        self._return_walk = [
+#                [('spam', (), ('file1', 'file2', 'file3', 'file4')),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                ]
+#        def filter_by_name(filename, results=[True, True, True, True]):
+#            self.calls.append(('filter_by_name', (filename,)))
+#            return results.pop(0)
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [
+#            fixpath('spam/file1'),
+#            fixpath('spam/file2'),
+#            fixpath('spam/file3'),
+#            fixpath('spam/file4'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ('filter_by_name', ('file1',)),
+#            ('filter_by_name', ('file2',)),
+#            ('filter_by_name', ('file3',)),
+#            ('filter_by_name', ('file4',)),
+#            ])
+#
+#    def test_all_filtered(self):
+#        self._return_walk = [
+#                [('spam', (), ('file1', 'file2', 'file3', 'file4')),
+#                 ],
+#                ]
+#        dirnames = [
+#                'spam',
+#                ]
+#        def filter_by_name(filename, results=[False, False, False, False]):
+#            self.calls.append(('filter_by_name', (filename,)))
+#            return results.pop(0)
+#
+#        files = list(iter_files(dirnames, filter_by_name,
+#                                _walk=self._walk))
+#
+#        self.assertEqual(files, [])
+#        self.assertEqual(self.calls, [
+#            ('_walk', ('spam',)),
+#            ('filter_by_name', ('file1',)),
+#            ('filter_by_name', ('file2',)),
+#            ('filter_by_name', ('file3',)),
+#            ('filter_by_name', ('file4',)),
+#            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_info.py
new file mode 100644 (file)
index 0000000..2d38671
--- /dev/null
@@ -0,0 +1,194 @@
+import string
+import unittest
+
+from ..util import PseudoStr, StrProxy, Object
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common.info import ID
+
+
+class IDTests(unittest.TestCase):
+
+    VALID_ARGS = (
+            'x/y/z/spam.c',
+            'func',
+            'eggs',
+            )
+    VALID_KWARGS = dict(zip(ID._fields, VALID_ARGS))
+    VALID_EXPECTED = VALID_ARGS
+
+    def test_from_raw(self):
+        tests = [
+            ('', None),
+            (None, None),
+            ('spam', (None, None, 'spam')),
+            (('spam',), (None, None, 'spam')),
+            (('x/y/z/spam.c', 'spam'), ('x/y/z/spam.c', None, 'spam')),
+            (self.VALID_ARGS, self.VALID_EXPECTED),
+            (self.VALID_KWARGS, self.VALID_EXPECTED),
+            ]
+        for raw, expected in tests:
+            with self.subTest(raw):
+                id = ID.from_raw(raw)
+
+                self.assertEqual(id, expected)
+
+    def test_minimal(self):
+        id = ID(
+                filename=None,
+                funcname=None,
+                name='eggs',
+                )
+
+        self.assertEqual(id, (
+                None,
+                None,
+                'eggs',
+                ))
+
+    def test_init_typical_global(self):
+        id = ID(
+                filename='x/y/z/spam.c',
+                funcname=None,
+                name='eggs',
+                )
+
+        self.assertEqual(id, (
+                'x/y/z/spam.c',
+                None,
+                'eggs',
+                ))
+
+    def test_init_typical_local(self):
+        id = ID(
+                filename='x/y/z/spam.c',
+                funcname='func',
+                name='eggs',
+                )
+
+        self.assertEqual(id, (
+                'x/y/z/spam.c',
+                'func',
+                'eggs',
+                ))
+
+    def test_init_all_missing(self):
+        for value in ('', None):
+            with self.subTest(repr(value)):
+                id = ID(
+                        filename=value,
+                        funcname=value,
+                        name=value,
+                        )
+
+                self.assertEqual(id, (
+                        None,
+                        None,
+                        None,
+                        ))
+
+    def test_init_all_coerced(self):
+        tests = [
+            ('str subclass',
+             dict(
+                 filename=PseudoStr('x/y/z/spam.c'),
+                 funcname=PseudoStr('func'),
+                 name=PseudoStr('eggs'),
+                 ),
+             ('x/y/z/spam.c',
+              'func',
+              'eggs',
+              )),
+            ('non-str',
+             dict(
+                 filename=StrProxy('x/y/z/spam.c'),
+                 funcname=Object(),
+                 name=('a', 'b', 'c'),
+                 ),
+             ('x/y/z/spam.c',
+              '<object>',
+              "('a', 'b', 'c')",
+              )),
+            ]
+        for summary, kwargs, expected in tests:
+            with self.subTest(summary):
+                id = ID(**kwargs)
+
+                for field in ID._fields:
+                    value = getattr(id, field)
+                    self.assertIs(type(value), str)
+                self.assertEqual(tuple(id), expected)
+
+    def test_iterable(self):
+        id = ID(**self.VALID_KWARGS)
+
+        filename, funcname, name = id
+
+        values = (filename, funcname, name)
+        for value, expected in zip(values, self.VALID_EXPECTED):
+            self.assertEqual(value, expected)
+
+    def test_fields(self):
+        id = ID('a', 'b', 'z')
+
+        self.assertEqual(id.filename, 'a')
+        self.assertEqual(id.funcname, 'b')
+        self.assertEqual(id.name, 'z')
+
+    def test_validate_typical(self):
+        id = ID(
+                filename='x/y/z/spam.c',
+                funcname='func',
+                name='eggs',
+                )
+
+        id.validate()  # This does not fail.
+
+    def test_validate_missing_field(self):
+        for field in ID._fields:
+            with self.subTest(field):
+                id = ID(**self.VALID_KWARGS)
+                id = id._replace(**{field: None})
+
+                if field == 'funcname':
+                    id.validate()  # The field can be missing (not set).
+                    id = id._replace(filename=None)
+                    id.validate()  # Both fields can be missing (not set).
+                    continue
+
+                with self.assertRaises(TypeError):
+                    id.validate()
+
+    def test_validate_bad_field(self):
+        badch = tuple(c for c in string.punctuation + string.digits)
+        notnames = (
+                '1a',
+                'a.b',
+                'a-b',
+                '&a',
+                'a++',
+                ) + badch
+        tests = [
+            ('filename', ()),  # Any non-empty str is okay.
+            ('funcname', notnames),
+            ('name', notnames),
+            ]
+        seen = set()
+        for field, invalid in tests:
+            for value in invalid:
+                seen.add(value)
+                with self.subTest(f'{field}={value!r}'):
+                    id = ID(**self.VALID_KWARGS)
+                    id = id._replace(**{field: value})
+
+                    with self.assertRaises(ValueError):
+                        id.validate()
+
+        for field, invalid in tests:
+            valid = seen - set(invalid)
+            for value in valid:
+                with self.subTest(f'{field}={value!r}'):
+                    id = ID(**self.VALID_KWARGS)
+                    id = id._replace(**{field: value})
+
+                    id.validate()  # This does not fail.
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_known.py b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_known.py
new file mode 100644 (file)
index 0000000..215023d
--- /dev/null
@@ -0,0 +1,68 @@
+import re
+import textwrap
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_parser.info import Variable
+    from c_analyzer_common.info import ID
+    from c_analyzer_common.known import from_file
+
+
+class FromFileTests(unittest.TestCase):
+
+    maxDiff = None
+
+    _return_read_tsv = ()
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+    def _read_tsv(self, *args):
+        self.calls.append(('_read_tsv', args))
+        return self._return_read_tsv
+
+    def test_typical(self):
+        lines = textwrap.dedent('''
+            filename    funcname        name    kind    declaration
+            file1.c     -       var1    variable        static int
+            file1.c     func1   local1  variable        static int
+            file1.c     -       var2    variable        int
+            file1.c     func2   local2  variable        char *
+            file2.c     -       var1    variable        char *
+            ''').strip().splitlines()
+        lines = [re.sub(r'\s+', '\t', line, 4) for line in lines]
+        self._return_read_tsv = [tuple(v.strip() for v in line.split('\t'))
+                                 for line in lines[1:]]
+
+        known = from_file('spam.c', _read_tsv=self._read_tsv)
+
+        self.assertEqual(known, {
+            'variables': {v.id: v for v in [
+                Variable.from_parts('file1.c', '', 'var1', 'static int'),
+                Variable.from_parts('file1.c', 'func1', 'local1', 'static int'),
+                Variable.from_parts('file1.c', '', 'var2', 'int'),
+                Variable.from_parts('file1.c', 'func2', 'local2', 'char *'),
+                Variable.from_parts('file2.c', '', 'var1', 'char *'),
+                ]},
+            })
+        self.assertEqual(self.calls, [
+            ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\tdeclaration')),
+            ])
+
+    def test_empty(self):
+        self._return_read_tsv = []
+
+        known = from_file('spam.c', _read_tsv=self._read_tsv)
+
+        self.assertEqual(known, {
+            'variables': {},
+            })
+        self.assertEqual(self.calls, [
+            ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\tdeclaration')),
+            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/test___main__.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test___main__.py
new file mode 100644 (file)
index 0000000..5f52c58
--- /dev/null
@@ -0,0 +1,296 @@
+import sys
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common import SOURCE_DIRS
+    from c_analyzer_common.known import DATA_FILE as KNOWN_FILE
+    from c_parser import info
+    import c_globals as cg
+    from c_globals.supported import IGNORED_FILE
+    from c_globals.__main__ import cmd_check, cmd_show, parse_args, main
+
+
+TYPICAL = [
+        (info.Variable.from_parts('src1/spam.c', None, 'var1', 'const char *'),
+         True,
+         ),
+        (info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'int'),
+         True,
+         ),
+        (info.Variable.from_parts('src1/spam.c', None, 'var2', 'PyObject *'),
+         False,
+         ),
+        (info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'int'),
+         True,
+         ),
+        (info.Variable.from_parts('src1/spam.c', None, 'freelist', '(PyTupleObject *)[10]'),
+         False,
+         ),
+        (info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'const char const *'),
+         True,
+         ),
+        (info.Variable.from_parts('src2/jam.c', None, 'var1', 'int'),
+         True,
+         ),
+        (info.Variable.from_parts('src2/jam.c', None, 'var2', 'MyObject *'),
+         False,
+         ),
+        (info.Variable.from_parts('Include/spam.h', None, 'data', 'const int'),
+         True,
+         ),
+        ]
+
+
+class CMDBase(unittest.TestCase):
+
+    maxDiff = None
+
+    _return_find = ()
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+    def _find(self, *args):
+        self.calls.append(('_find', args))
+        return self._return_find
+
+    def _show(self, *args):
+        self.calls.append(('_show', args))
+
+    def _print(self, *args):
+        self.calls.append(('_print', args))
+
+
+class CheckTests(CMDBase):
+
+    def test_defaults(self):
+        self._return_find = []
+
+        cmd_check('check',
+                  _find=self._find,
+                  _show=self._show,
+                  _print=self._print,
+                  )
+
+        self.assertEqual(self.calls[0], (
+            '_find', (
+                SOURCE_DIRS,
+                KNOWN_FILE,
+                IGNORED_FILE,
+                ),
+            ))
+
+    def test_all_supported(self):
+        self._return_find = [(v, s) for v, s in TYPICAL if s]
+        dirs = ['src1', 'src2', 'Include']
+
+        cmd_check('check',
+                 dirs,
+                 ignored='ignored.tsv',
+                 known='known.tsv',
+                 _find=self._find,
+                 _show=self._show,
+                 _print=self._print,
+                 )
+
+        self.assertEqual(self.calls, [
+            ('_find', (dirs, 'known.tsv', 'ignored.tsv')),
+            #('_print', ('okay',)),
+            ])
+
+    def test_some_unsupported(self):
+        self._return_find = TYPICAL
+        dirs = ['src1', 'src2', 'Include']
+
+        with self.assertRaises(SystemExit) as cm:
+            cmd_check('check',
+                      dirs,
+                      ignored='ignored.tsv',
+                      known='known.tsv',
+                      _find=self._find,
+                      _show=self._show,
+                      _print=self._print,
+                      )
+
+        unsupported = [v for v, s in TYPICAL if not s]
+        self.assertEqual(self.calls, [
+            ('_find', (dirs, 'known.tsv', 'ignored.tsv')),
+            ('_print', ('ERROR: found unsupported global variables',)),
+            ('_print', ()),
+            ('_show', (sorted(unsupported),)),
+            ('_print', (' (3 total)',)),
+            ])
+        self.assertEqual(cm.exception.code, 1)
+
+
+class ShowTests(CMDBase):
+
+    def test_defaults(self):
+        self._return_find = []
+
+        cmd_show('show',
+                 _find=self._find,
+                 _show=self._show,
+                 _print=self._print,
+                 )
+
+        self.assertEqual(self.calls[0], (
+            '_find', (
+                SOURCE_DIRS,
+                KNOWN_FILE,
+                IGNORED_FILE,
+                ),
+            ))
+
+    def test_typical(self):
+        self._return_find = TYPICAL
+        dirs = ['src1', 'src2', 'Include']
+
+        cmd_show('show',
+                 dirs,
+                 known='known.tsv',
+                 ignored='ignored.tsv',
+                 _find=self._find,
+                 _show=self._show,
+                 _print=self._print,
+                 )
+
+        supported = [v for v, s in TYPICAL if s]
+        unsupported = [v for v, s in TYPICAL if not s]
+        self.assertEqual(self.calls, [
+            ('_find', (dirs, 'known.tsv', 'ignored.tsv')),
+            ('_print', ('supported:',)),
+            ('_print', ('----------',)),
+            ('_show', (sorted(supported),)),
+            ('_print', (' (6 total)',)),
+            ('_print', ()),
+            ('_print', ('unsupported:',)),
+            ('_print', ('------------',)),
+            ('_show', (sorted(unsupported),)),
+            ('_print', (' (3 total)',)),
+            ])
+
+
+class ParseArgsTests(unittest.TestCase):
+
+    maxDiff = None
+
+    def test_no_args(self):
+        self.errmsg = None
+        def fail(msg):
+            self.errmsg = msg
+            sys.exit(msg)
+
+        with self.assertRaises(SystemExit):
+            parse_args('cg', [], _fail=fail)
+
+        self.assertEqual(self.errmsg, 'missing command')
+
+    def test_check_no_args(self):
+        cmd, cmdkwargs = parse_args('cg', [
+            'check',
+            ])
+
+        self.assertEqual(cmd, 'check')
+        self.assertEqual(cmdkwargs, {
+            'ignored': IGNORED_FILE,
+            'known': KNOWN_FILE,
+            'dirs': SOURCE_DIRS,
+            })
+
+    def test_check_full_args(self):
+        cmd, cmdkwargs = parse_args('cg', [
+            'check',
+            '--ignored', 'spam.tsv',
+            '--known', 'eggs.tsv',
+            'dir1',
+            'dir2',
+            'dir3',
+            ])
+
+        self.assertEqual(cmd, 'check')
+        self.assertEqual(cmdkwargs, {
+            'ignored': 'spam.tsv',
+            'known': 'eggs.tsv',
+            'dirs': ['dir1', 'dir2', 'dir3']
+            })
+
+    def test_show_no_args(self):
+        cmd, cmdkwargs = parse_args('cg', [
+            'show',
+            ])
+
+        self.assertEqual(cmd, 'show')
+        self.assertEqual(cmdkwargs, {
+            'ignored': IGNORED_FILE,
+            'known': KNOWN_FILE,
+            'dirs': SOURCE_DIRS,
+            'skip_objects': False,
+            })
+
+    def test_show_full_args(self):
+        cmd, cmdkwargs = parse_args('cg', [
+            'show',
+            '--ignored', 'spam.tsv',
+            '--known', 'eggs.tsv',
+            'dir1',
+            'dir2',
+            'dir3',
+            ])
+
+        self.assertEqual(cmd, 'show')
+        self.assertEqual(cmdkwargs, {
+            'ignored': 'spam.tsv',
+            'known': 'eggs.tsv',
+            'dirs': ['dir1', 'dir2', 'dir3'],
+            'skip_objects': False,
+            })
+
+
+def new_stub_commands(*names):
+    calls = []
+    def cmdfunc(cmd, **kwargs):
+        calls.append((cmd, kwargs))
+    commands = {name: cmdfunc for name in names}
+    return commands, calls
+
+
+class MainTests(unittest.TestCase):
+
+    def test_no_command(self):
+        with self.assertRaises(ValueError):
+            main(None, {})
+
+    def test_check(self):
+        commands, calls = new_stub_commands('check', 'show')
+
+        cmdkwargs = {
+            'ignored': 'spam.tsv',
+            'known': 'eggs.tsv',
+            'dirs': ['dir1', 'dir2', 'dir3'],
+            }
+        main('check', cmdkwargs, _COMMANDS=commands)
+
+        self.assertEqual(calls, [
+            ('check', cmdkwargs),
+            ])
+
+    def test_show(self):
+        commands, calls = new_stub_commands('check', 'show')
+
+        cmdkwargs = {
+            'ignored': 'spam.tsv',
+            'known': 'eggs.tsv',
+            'dirs': ['dir1', 'dir2', 'dir3'],
+            }
+        main('show', cmdkwargs, _COMMANDS=commands)
+
+        self.assertEqual(calls, [
+            ('show', cmdkwargs),
+            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_find.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_find.py
new file mode 100644 (file)
index 0000000..b29f966
--- /dev/null
@@ -0,0 +1,332 @@
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_parser import info
+    from c_globals.find import globals_from_binary, globals
+
+
+class _Base(unittest.TestCase):
+
+    maxDiff = None
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+
+class StaticsFromBinaryTests(_Base):
+
+    _return_iter_symbols = ()
+    _return_resolve_symbols = ()
+    _return_get_symbol_resolver = None
+
+    def setUp(self):
+        super().setUp()
+
+        self.kwargs = dict(
+                _iter_symbols=self._iter_symbols,
+                _resolve=self._resolve_symbols,
+                _get_symbol_resolver=self._get_symbol_resolver,
+                )
+
+    def _iter_symbols(self, binfile, find_local_symbol):
+        self.calls.append(('_iter_symbols', (binfile, find_local_symbol)))
+        return self._return_iter_symbols
+
+    def _resolve_symbols(self, symbols, resolve):
+        self.calls.append(('_resolve_symbols', (symbols, resolve,)))
+        return self._return_resolve_symbols
+
+    def _get_symbol_resolver(self, knownvars, dirnames=None):
+        self.calls.append(('_get_symbol_resolver', (knownvars, dirnames)))
+        return self._return_get_symbol_resolver
+
+    def test_typical(self):
+        symbols = self._return_iter_symbols = ()
+        resolver = self._return_get_symbol_resolver = object()
+        variables = self._return_resolve_symbols = [
+            info.Variable.from_parts('dir1/spam.c', None, 'var1', 'int'),
+            info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'),
+            info.Variable.from_parts('dir1/spam.c', None, 'var3', 'char *'),
+            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', 'const char *'),
+            info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'),
+            info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'),
+            ]
+        knownvars = object()
+
+        found = list(globals_from_binary('python',
+                                         knownvars=knownvars,
+                                         **self.kwargs))
+
+        self.assertEqual(found, [
+            info.Variable.from_parts('dir1/spam.c', None, 'var2', 'static int'),
+            info.Variable.from_parts('dir1/eggs.c', None, 'var1', 'static int'),
+            info.Variable.from_parts('dir1/eggs.c', 'func1', 'var2', 'static char *'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_iter_symbols', ('python', None)),
+            ('_get_symbol_resolver', (knownvars, None)),
+            ('_resolve_symbols', (symbols, resolver)),
+            ])
+
+#        self._return_iter_symbols = [
+#                s_info.Symbol(('dir1/spam.c', None, 'var1'), 'variable', False),
+#                s_info.Symbol(('dir1/spam.c', None, 'var2'), 'variable', False),
+#                s_info.Symbol(('dir1/spam.c', None, 'func1'), 'function', False),
+#                s_info.Symbol(('dir1/spam.c', None, 'func2'), 'function', True),
+#                s_info.Symbol(('dir1/spam.c', None, 'var3'), 'variable', False),
+#                s_info.Symbol(('dir1/spam.c', 'func2', 'var4'), 'variable', False),
+#                s_info.Symbol(('dir1/ham.c', None, 'var1'), 'variable', True),
+#                s_info.Symbol(('dir1/eggs.c', None, 'var1'), 'variable', False),
+#                s_info.Symbol(('dir1/eggs.c', None, 'xyz'), 'other', False),
+#                s_info.Symbol(('dir1/eggs.c', '???', 'var2'), 'variable', False),
+#                s_info.Symbol(('???', None, 'var_x'), 'variable', False),
+#                s_info.Symbol(('???', '???', 'var_y'), 'variable', False),
+#                s_info.Symbol((None, None, '???'), 'other', False),
+#                ]
+#        known = object()
+#
+#        globals_from_binary('python', knownvars=known, **this.kwargs)
+#        found = list(globals_from_symbols(['dir1'], self.iter_symbols))
+#
+#        self.assertEqual(found, [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('iter_symbols', (['dir1'],)),
+#            ])
+#
+#    def test_no_symbols(self):
+#        self._return_iter_symbols = []
+#
+#        found = list(globals_from_symbols(['dir1'], self.iter_symbols))
+#
+#        self.assertEqual(found, [])
+#        self.assertEqual(self.calls, [
+#            ('iter_symbols', (['dir1'],)),
+#            ])
+
+    # XXX need functional test
+
+
+#class StaticFromDeclarationsTests(_Base):
+#
+#    _return_iter_declarations = ()
+#
+#    def iter_declarations(self, dirnames):
+#        self.calls.append(('iter_declarations', (dirnames,)))
+#        return iter(self._return_iter_declarations)
+#
+#    def test_typical(self):
+#        self._return_iter_declarations = [
+#            None,
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            object(),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            object(),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            object(),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            object(),
+#            ]
+#
+#        found = list(globals_from_declarations(['dir1'], self.iter_declarations))
+#
+#        self.assertEqual(found, [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ])
+#        self.assertEqual(self.calls, [
+#            ('iter_declarations', (['dir1'],)),
+#            ])
+#
+#    def test_no_declarations(self):
+#        self._return_iter_declarations = []
+#
+#        found = list(globals_from_declarations(['dir1'], self.iter_declarations))
+#
+#        self.assertEqual(found, [])
+#        self.assertEqual(self.calls, [
+#            ('iter_declarations', (['dir1'],)),
+#            ])
+
+
+#class IterVariablesTests(_Base):
+#
+#    _return_from_symbols = ()
+#    _return_from_declarations = ()
+#
+#    def _from_symbols(self, dirnames, iter_symbols):
+#        self.calls.append(('_from_symbols', (dirnames, iter_symbols)))
+#        return iter(self._return_from_symbols)
+#
+#    def _from_declarations(self, dirnames, iter_declarations):
+#        self.calls.append(('_from_declarations', (dirnames, iter_declarations)))
+#        return iter(self._return_from_declarations)
+#
+#    def test_typical(self):
+#        expected = [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ]
+#        self._return_from_symbols = expected
+#
+#        found = list(iter_variables(['dir1'],
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, expected)
+#        self.assertEqual(self.calls, [
+#            ('_from_symbols', (['dir1'], b_symbols.iter_symbols)),
+#            ])
+#
+#    def test_no_symbols(self):
+#        self._return_from_symbols = []
+#
+#        found = list(iter_variables(['dir1'],
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, [])
+#        self.assertEqual(self.calls, [
+#            ('_from_symbols', (['dir1'], b_symbols.iter_symbols)),
+#            ])
+#
+#    def test_from_binary(self):
+#        expected = [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ]
+#        self._return_from_symbols = expected
+#
+#        found = list(iter_variables(['dir1'], 'platform',
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, expected)
+#        self.assertEqual(self.calls, [
+#            ('_from_symbols', (['dir1'], b_symbols.iter_symbols)),
+#            ])
+#
+#    def test_from_symbols(self):
+#        expected = [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ]
+#        self._return_from_symbols = expected
+#
+#        found = list(iter_variables(['dir1'], 'symbols',
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, expected)
+#        self.assertEqual(self.calls, [
+#            ('_from_symbols', (['dir1'], s_symbols.iter_symbols)),
+#            ])
+#
+#    def test_from_declarations(self):
+#        expected = [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ]
+#        self._return_from_declarations = expected
+#
+#        found = list(iter_variables(['dir1'], 'declarations',
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, expected)
+#        self.assertEqual(self.calls, [
+#            ('_from_declarations', (['dir1'], declarations.iter_all)),
+#            ])
+#
+#    def test_from_preprocessed(self):
+#        expected = [
+#            info.Variable.from_parts('dir1/spam.c', None, 'var1', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var2', '???'),
+#            info.Variable.from_parts('dir1/spam.c', None, 'var3', '???'),
+#            info.Variable.from_parts('dir1/spam.c', 'func2', 'var4', '???'),
+#            info.Variable.from_parts('dir1/eggs.c', None, 'var1', '???'),
+#            ]
+#        self._return_from_declarations = expected
+#
+#        found = list(iter_variables(['dir1'], 'preprocessed',
+#                                  _from_symbols=self._from_symbols,
+#                                  _from_declarations=self._from_declarations))
+#
+#        self.assertEqual(found, expected)
+#        self.assertEqual(self.calls, [
+#            ('_from_declarations', (['dir1'], declarations.iter_preprocessed)),
+#            ])
+
+
+class StaticsTest(_Base):
+
+    _return_iter_variables = None
+
+    def _iter_variables(self, kind, *, known, dirnames):
+        self.calls.append(
+                ('_iter_variables', (kind, known, dirnames)))
+        return iter(self._return_iter_variables or ())
+
+    def test_typical(self):
+        self._return_iter_variables = [
+            info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'),
+            info.Variable.from_parts('src1/spam.c', None, 'var1b', 'const char *'),
+            info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'),
+            info.Variable.from_parts('src1/spam.c', 'ham', 'result', 'int'),
+            info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'),
+            info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'),
+            info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'),
+            info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'static const char const *'),
+            info.Variable.from_parts('src2/jam.c', None, 'var1', 'static int'),
+            info.Variable.from_parts('src2/jam.c', None, 'var2', 'static MyObject *'),
+            info.Variable.from_parts('Include/spam.h', None, 'data', 'static const int'),
+            ]
+        dirnames = object()
+        known = object()
+
+        found = list(globals(dirnames, known,
+                             kind='platform',
+                             _iter_variables=self._iter_variables,
+                             ))
+
+        self.assertEqual(found, [
+            info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'),
+            info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'),
+            info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'),
+            info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'),
+            info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'),
+            info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'static const char const *'),
+            info.Variable.from_parts('src2/jam.c', None, 'var1', 'static int'),
+            info.Variable.from_parts('src2/jam.c', None, 'var2', 'static MyObject *'),
+            info.Variable.from_parts('Include/spam.h', None, 'data', 'static const int'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_iter_variables', ('platform', known, dirnames)),
+            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_functional.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_functional.py
new file mode 100644 (file)
index 0000000..9279790
--- /dev/null
@@ -0,0 +1,34 @@
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    pass
+
+
+class SelfCheckTests(unittest.TestCase):
+
+    @unittest.expectedFailure
+    def test_known(self):
+        # Make sure known macros & vartypes aren't hiding unknown local types.
+        # XXX finish!
+        raise NotImplementedError
+
+    @unittest.expectedFailure
+    def test_compare_nm_results(self):
+        # Make sure the "show" results match the statics found by "nm" command.
+        # XXX Skip if "nm" is not available.
+        # XXX finish!
+        raise NotImplementedError
+
+
+class DummySourceTests(unittest.TestCase):
+
+    @unittest.expectedFailure
+    def test_check(self):
+        # XXX finish!
+        raise NotImplementedError
+
+    @unittest.expectedFailure
+    def test_show(self):
+        # XXX finish!
+        raise NotImplementedError
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_show.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_show.py
new file mode 100644 (file)
index 0000000..ce1dad8
--- /dev/null
@@ -0,0 +1,52 @@
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_parser import info
+    from c_globals.show import basic
+
+
+TYPICAL = [
+        info.Variable.from_parts('src1/spam.c', None, 'var1', 'static const char *'),
+        info.Variable.from_parts('src1/spam.c', 'ham', 'initialized', 'static int'),
+        info.Variable.from_parts('src1/spam.c', None, 'var2', 'static PyObject *'),
+        info.Variable.from_parts('src1/eggs.c', 'tofu', 'ready', 'static int'),
+        info.Variable.from_parts('src1/spam.c', None, 'freelist', 'static (PyTupleObject *)[10]'),
+        info.Variable.from_parts('src1/sub/ham.c', None, 'var1', 'static const char const *'),
+        info.Variable.from_parts('src2/jam.c', None, 'var1', 'static int'),
+        info.Variable.from_parts('src2/jam.c', None, 'var2', 'static MyObject *'),
+        info.Variable.from_parts('Include/spam.h', None, 'data', 'static const int'),
+        ]
+
+
+class BasicTests(unittest.TestCase):
+
+    maxDiff = None
+
+    def setUp(self):
+        self.lines = []
+
+    def print(self, line):
+        self.lines.append(line)
+
+    def test_typical(self):
+        basic(TYPICAL,
+              _print=self.print)
+
+        self.assertEqual(self.lines, [
+            'src1/spam.c:var1                                                 static const char *',
+            'src1/spam.c:ham():initialized                                    static int',
+            'src1/spam.c:var2                                                 static PyObject *',
+            'src1/eggs.c:tofu():ready                                         static int',
+            'src1/spam.c:freelist                                             static (PyTupleObject *)[10]',
+            'src1/sub/ham.c:var1                                              static const char const *',
+            'src2/jam.c:var1                                                  static int',
+            'src2/jam.c:var2                                                  static MyObject *',
+            'Include/spam.h:data                                              static const int',
+            ])
+
+    def test_no_rows(self):
+        basic([],
+              _print=self.print)
+
+        self.assertEqual(self.lines, [])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_supported.py b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_supported.py
new file mode 100644 (file)
index 0000000..1e7d40e
--- /dev/null
@@ -0,0 +1,96 @@
+import re
+import textwrap
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common.info import ID
+    from c_parser import info
+    from c_globals.supported import is_supported, ignored_from_file
+
+
+class IsSupportedTests(unittest.TestCase):
+
+    @unittest.expectedFailure
+    def test_supported(self):
+        statics = [
+                info.StaticVar('src1/spam.c', None, 'var1', 'const char *'),
+                info.StaticVar('src1/spam.c', None, 'var1', 'int'),
+                ]
+        for static in statics:
+            with self.subTest(static):
+                result = is_supported(static)
+
+                self.assertTrue(result)
+
+    @unittest.expectedFailure
+    def test_not_supported(self):
+        statics = [
+                info.StaticVar('src1/spam.c', None, 'var1', 'PyObject *'),
+                info.StaticVar('src1/spam.c', None, 'var1', 'PyObject[10]'),
+                ]
+        for static in statics:
+            with self.subTest(static):
+                result = is_supported(static)
+
+                self.assertFalse(result)
+
+
+class IgnoredFromFileTests(unittest.TestCase):
+
+    maxDiff = None
+
+    _return_read_tsv = ()
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+    def _read_tsv(self, *args):
+        self.calls.append(('_read_tsv', args))
+        return self._return_read_tsv
+
+    def test_typical(self):
+        lines = textwrap.dedent('''
+            filename    funcname        name    kind    reason
+            file1.c     -       var1    variable        ...
+            file1.c     func1   local1  variable        |
+            file1.c     -       var2    variable        ???
+            file1.c     func2   local2  variable           |
+            file2.c     -       var1    variable        reasons
+            ''').strip().splitlines()
+        lines = [re.sub(r'\s{1,8}', '\t', line, 4).replace('|', '')
+                 for line in lines]
+        self._return_read_tsv = [tuple(v.strip() for v in line.split('\t'))
+                                 for line in lines[1:]]
+
+        ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv)
+
+        self.assertEqual(ignored, {
+            'variables': {
+                ID('file1.c', '', 'var1'): '...',
+                ID('file1.c', 'func1', 'local1'): '',
+                ID('file1.c', '', 'var2'): '???',
+                ID('file1.c', 'func2', 'local2'): '',
+                ID('file2.c', '', 'var1'): 'reasons',
+                },
+            })
+        self.assertEqual(self.calls, [
+            ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')),
+            ])
+
+    def test_empty(self):
+        self._return_read_tsv = []
+
+        ignored = ignored_from_file('spam.c', _read_tsv=self._read_tsv)
+
+        self.assertEqual(ignored, {
+            'variables': {},
+            })
+        self.assertEqual(self.calls, [
+            ('_read_tsv', ('spam.c', 'filename\tfuncname\tname\tkind\treason')),
+            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_parser/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_c_parser/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_declarations.py b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_declarations.py
new file mode 100644 (file)
index 0000000..b68744e
--- /dev/null
@@ -0,0 +1,795 @@
+import textwrap
+import unittest
+
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_parser.declarations import (
+        iter_global_declarations, iter_local_statements,
+        parse_func, parse_var, parse_compound,
+        iter_variables,
+        )
+
+
+class TestCaseBase(unittest.TestCase):
+
+    maxDiff = None
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+
+class IterGlobalDeclarationsTests(TestCaseBase):
+
+    def test_functions(self):
+        tests = [
+            (textwrap.dedent('''
+                void func1() {
+                    return;
+                }
+                '''),
+             textwrap.dedent('''
+                void func1() {
+                return;
+                }
+                ''').strip(),
+             ),
+            (textwrap.dedent('''
+                static unsigned int * _func1(
+                    const char *arg1,
+                    int *arg2
+                    long long arg3
+                    )
+                {
+                    return _do_something(arg1, arg2, arg3);
+                }
+                '''),
+             textwrap.dedent('''
+                static unsigned int * _func1( const char *arg1, int *arg2 long long arg3 ) {
+                return _do_something(arg1, arg2, arg3);
+                }
+                ''').strip(),
+             ),
+            (textwrap.dedent('''
+                static PyObject *
+                _func1(const char *arg1, PyObject *arg2)
+                {
+                    static int initialized = 0;
+                    if (!initialized) {
+                        initialized = 1;
+                        _init(arg1);
+                    }
+
+                    PyObject *result = _do_something(arg1, arg2);
+                    Py_INCREF(result);
+                    return result;
+                }
+                '''),
+             textwrap.dedent('''
+                static PyObject * _func1(const char *arg1, PyObject *arg2) {
+                static int initialized = 0;
+                if (!initialized) {
+                initialized = 1;
+                _init(arg1);
+                }
+                PyObject *result = _do_something(arg1, arg2);
+                Py_INCREF(result);
+                return result;
+                }
+                ''').strip(),
+             ),
+            ]
+        for lines, expected in tests:
+            body = textwrap.dedent(
+                    expected.partition('{')[2].rpartition('}')[0]
+                    ).strip()
+            expected = (expected, body)
+            with self.subTest(lines):
+                lines = lines.splitlines()
+
+                stmts = list(iter_global_declarations(lines))
+
+                self.assertEqual(stmts, [expected])
+
+    @unittest.expectedFailure
+    def test_declarations(self):
+        tests = [
+            'int spam;',
+            'long long spam;',
+            'static const int const *spam;',
+            'int spam;',
+            'typedef int myint;',
+            'typedef PyObject * (*unaryfunc)(PyObject *);',
+            # typedef struct
+            # inline struct
+            # enum
+            # inline enum
+            ]
+        for text in tests:
+            expected = (text,
+                        ' '.join(l.strip() for l in text.splitlines()))
+            with self.subTest(lines):
+                lines = lines.splitlines()
+
+                stmts = list(iter_global_declarations(lines))
+
+                self.assertEqual(stmts, [expected])
+
+    @unittest.expectedFailure
+    def test_declaration_multiple_vars(self):
+        lines = ['static const int const *spam, *ham=NULL, eggs = 3;']
+
+        stmts = list(iter_global_declarations(lines))
+
+        self.assertEqual(stmts, [
+            ('static const int const *spam;', None),
+            ('static const int *ham=NULL;', None),
+            ('static const int eggs = 3;', None),
+            ])
+
+    def test_mixed(self):
+        lines = textwrap.dedent('''
+           int spam;
+           static const char const *eggs;
+
+           PyObject * start(void) {
+               static int initialized = 0;
+               if (initialized) {
+                   initialized = 1;
+                   init();
+               }
+               return _start();
+           }
+
+           char* ham;
+
+           static int stop(char *reason) {
+               ham = reason;
+               return _stop();
+           }
+           ''').splitlines()
+        expected = [
+            (textwrap.dedent('''
+                PyObject * start(void) {
+                static int initialized = 0;
+                if (initialized) {
+                initialized = 1;
+                init();
+                }
+                return _start();
+                }
+                ''').strip(),
+             textwrap.dedent('''
+                static int initialized = 0;
+                if (initialized) {
+                initialized = 1;
+                init();
+                }
+                return _start();
+                ''').strip(),
+             ),
+            (textwrap.dedent('''
+                static int stop(char *reason) {
+                ham = reason;
+                return _stop();
+                }
+                ''').strip(),
+             textwrap.dedent('''
+                ham = reason;
+                return _stop();
+                ''').strip(),
+             ),
+            ]
+
+        stmts = list(iter_global_declarations(lines))
+
+        self.assertEqual(stmts, expected)
+        #self.assertEqual([stmt for stmt, _ in stmts],
+        #                 [stmt for stmt, _ in expected])
+        #self.assertEqual([body for _, body in stmts],
+        #                 [body for _, body in expected])
+
+    def test_no_statements(self):
+        lines = []
+
+        stmts = list(iter_global_declarations(lines))
+
+        self.assertEqual(stmts, [])
+
+    def test_bogus(self):
+        tests = [
+                (textwrap.dedent('''
+                    int spam;
+                    static const char const *eggs;
+
+                    PyObject * start(void) {
+                        static int initialized = 0;
+                        if (initialized) {
+                            initialized = 1;
+                            init();
+                        }
+                        return _start();
+                    }
+
+                    char* ham;
+
+                    static int _stop(void) {
+                    // missing closing bracket
+
+                    static int stop(char *reason) {
+                        ham = reason;
+                        return _stop();
+                    }
+                    '''),
+                 [(textwrap.dedent('''
+                    PyObject * start(void) {
+                    static int initialized = 0;
+                    if (initialized) {
+                    initialized = 1;
+                    init();
+                    }
+                    return _start();
+                    }
+                    ''').strip(),
+                   textwrap.dedent('''
+                    static int initialized = 0;
+                    if (initialized) {
+                    initialized = 1;
+                    init();
+                    }
+                    return _start();
+                    ''').strip(),
+                   ),
+                   # Neither "stop()" nor "_stop()" are here.
+                  ],
+                 ),
+                ]
+        for lines, expected in tests:
+            with self.subTest(lines):
+                lines = lines.splitlines()
+
+                stmts = list(iter_global_declarations(lines))
+
+                self.assertEqual(stmts, expected)
+                #self.assertEqual([stmt for stmt, _ in stmts],
+                #                 [stmt for stmt, _ in expected])
+                #self.assertEqual([body for _, body in stmts],
+                #                 [body for _, body in expected])
+
+    def test_ignore_comments(self):
+        tests = [
+            ('// msg', None),
+            ('// int stmt;', None),
+            ('    // ...    ', None),
+            ('// /*', None),
+            ('/* int stmt; */', None),
+            ("""
+             /**
+              * ...
+              * int stmt;
+              */
+             """, None),
+            ]
+        for lines, expected in tests:
+            with self.subTest(lines):
+                lines = lines.splitlines()
+
+                stmts = list(iter_global_declarations(lines))
+
+                self.assertEqual(stmts, [expected] if expected else [])
+
+
+class IterLocalStatementsTests(TestCaseBase):
+
+    def test_vars(self):
+        tests = [
+            # POTS
+            'int spam;',
+            'unsigned int spam;',
+            'char spam;',
+            'float spam;',
+
+            # typedefs
+            'uint spam;',
+            'MyType spam;',
+
+            # complex
+            'struct myspam spam;',
+            'union choice spam;',
+            # inline struct
+            # inline union
+            # enum?
+            ]
+        # pointers
+        tests.extend([
+            # POTS
+            'int * spam;',
+            'unsigned int * spam;',
+            'char *spam;',
+            'char const *spam = "spamspamspam...";',
+            # typedefs
+            'MyType *spam;',
+            # complex
+            'struct myspam *spam;',
+            'union choice *spam;',
+            # packed with details
+            'const char const *spam;',
+            # void pointer
+            'void *data = NULL;',
+            # function pointers
+            'int (* func)(char *arg1);',
+            'char * (* func)(void);',
+            ])
+        # storage class
+        tests.extend([
+            'static int spam;',
+            'extern int spam;',
+            'static unsigned int spam;',
+            'static struct myspam spam;',
+            ])
+        # type qualifier
+        tests.extend([
+            'const int spam;',
+            'const unsigned int spam;',
+            'const struct myspam spam;',
+            ])
+        # combined
+        tests.extend([
+            'const char *spam = eggs;',
+            'static const char const *spam = "spamspamspam...";',
+            'extern const char const *spam;',
+            'static void *data = NULL;',
+            'static int (const * func)(char *arg1) = func1;',
+            'static char * (* func)(void);',
+            ])
+        for line in tests:
+            expected = line
+            with self.subTest(line):
+                stmts = list(iter_local_statements([line]))
+
+                self.assertEqual(stmts, [(expected, None)])
+
+    @unittest.expectedFailure
+    def test_vars_multiline_var(self):
+        lines = textwrap.dedent('''
+            PyObject *
+            spam
+            = NULL;
+            ''').splitlines()
+        expected = 'PyObject * spam = NULL;'
+
+        stmts = list(iter_local_statements(lines))
+
+        self.assertEqual(stmts, [(expected, None)])
+
+    @unittest.expectedFailure
+    def test_declaration_multiple_vars(self):
+        lines = ['static const int const *spam, *ham=NULL, ham2[]={1, 2, 3}, ham3[2]={1, 2}, eggs = 3;']
+
+        stmts = list(iter_global_declarations(lines))
+
+        self.assertEqual(stmts, [
+            ('static const int const *spam;', None),
+            ('static const int *ham=NULL;', None),
+            ('static const int ham[]={1, 2, 3};', None),
+            ('static const int ham[2]={1, 2};', None),
+            ('static const int eggs = 3;', None),
+            ])
+
+    @unittest.expectedFailure
+    def test_other_simple(self):
+        raise NotImplementedError
+
+    @unittest.expectedFailure
+    def test_compound(self):
+        raise NotImplementedError
+
+    @unittest.expectedFailure
+    def test_mixed(self):
+        raise NotImplementedError
+
+    def test_no_statements(self):
+        lines = []
+
+        stmts = list(iter_local_statements(lines))
+
+        self.assertEqual(stmts, [])
+
+    @unittest.expectedFailure
+    def test_bogus(self):
+        raise NotImplementedError
+
+    def test_ignore_comments(self):
+        tests = [
+            ('// msg', None),
+            ('// int stmt;', None),
+            ('    // ...    ', None),
+            ('// /*', None),
+            ('/* int stmt; */', None),
+            ("""
+             /**
+              * ...
+              * int stmt;
+              */
+             """, None),
+            # mixed with statements
+            ('int stmt; // ...', ('int stmt;', None)),
+            ( 'int stmt; /* ...  */', ('int stmt;', None)),
+            ( '/* ...  */ int stmt;', ('int stmt;', None)),
+            ]
+        for lines, expected in tests:
+            with self.subTest(lines):
+                lines = lines.splitlines()
+
+                stmts = list(iter_local_statements(lines))
+
+                self.assertEqual(stmts, [expected] if expected else [])
+
+
+class ParseFuncTests(TestCaseBase):
+
+    def test_typical(self):
+        tests = [
+            ('PyObject *\nspam(char *a)\n{\nreturn _spam(a);\n}',
+             'return _spam(a);',
+             ('spam', 'PyObject * spam(char *a)'),
+             ),
+            ]
+        for stmt, body, expected in tests:
+            with self.subTest(stmt):
+                name, signature = parse_func(stmt, body)
+
+                self.assertEqual((name, signature), expected)
+
+
+class ParseVarTests(TestCaseBase):
+
+    def test_typical(self):
+        tests = [
+            # POTS
+            ('int spam;', ('spam', 'int')),
+            ('unsigned int spam;', ('spam', 'unsigned int')),
+            ('char spam;', ('spam', 'char')),
+            ('float spam;', ('spam', 'float')),
+
+            # typedefs
+            ('uint spam;', ('spam', 'uint')),
+            ('MyType spam;', ('spam', 'MyType')),
+
+            # complex
+            ('struct myspam spam;', ('spam', 'struct myspam')),
+            ('union choice spam;', ('spam', 'union choice')),
+            # inline struct
+            # inline union
+            # enum?
+            ]
+        # pointers
+        tests.extend([
+            # POTS
+            ('int * spam;', ('spam', 'int *')),
+            ('unsigned int * spam;', ('spam', 'unsigned int *')),
+            ('char *spam;', ('spam', 'char *')),
+            ('char const *spam = "spamspamspam...";', ('spam', 'char const *')),
+            # typedefs
+            ('MyType *spam;', ('spam', 'MyType *')),
+            # complex
+            ('struct myspam *spam;', ('spam', 'struct myspam *')),
+            ('union choice *spam;', ('spam', 'union choice *')),
+            # packed with details
+            ('const char const *spam;', ('spam', 'const char const *')),
+            # void pointer
+            ('void *data = NULL;', ('data', 'void *')),
+            # function pointers
+            ('int (* func)(char *);', ('func', 'int (*)(char *)')),
+            ('char * (* func)(void);', ('func', 'char * (*)(void)')),
+            ])
+        # storage class
+        tests.extend([
+            ('static int spam;', ('spam', 'static int')),
+            ('extern int spam;', ('spam', 'extern int')),
+            ('static unsigned int spam;', ('spam', 'static unsigned int')),
+            ('static struct myspam spam;', ('spam', 'static struct myspam')),
+            ])
+        # type qualifier
+        tests.extend([
+            ('const int spam;', ('spam', 'const int')),
+            ('const unsigned int spam;', ('spam', 'const unsigned int')),
+            ('const struct myspam spam;', ('spam', 'const struct myspam')),
+            ])
+        # combined
+        tests.extend([
+            ('const char *spam = eggs;', ('spam', 'const char *')),
+            ('static const char const *spam = "spamspamspam...";',
+             ('spam', 'static const char const *')),
+            ('extern const char const *spam;',
+             ('spam', 'extern const char const *')),
+            ('static void *data = NULL;', ('data', 'static void *')),
+            ('static int (const * func)(char *) = func1;',
+             ('func', 'static int (const *)(char *)')),
+            ('static char * (* func)(void);',
+             ('func', 'static char * (*)(void)')),
+            ])
+        for stmt, expected in tests:
+            with self.subTest(stmt):
+                name, vartype = parse_var(stmt)
+
+                self.assertEqual((name, vartype), expected)
+
+
+@unittest.skip('not finished')
+class ParseCompoundTests(TestCaseBase):
+
+    def test_typical(self):
+        headers, bodies = parse_compound(stmt, blocks)
+        ...
+
+
+class IterVariablesTests(TestCaseBase):
+
+    _return_iter_source_lines = None
+    _return_iter_global = None
+    _return_iter_local = None
+    _return_parse_func = None
+    _return_parse_var = None
+    _return_parse_compound = None
+
+    def _iter_source_lines(self, filename):
+        self.calls.append(
+                ('_iter_source_lines', (filename,)))
+        return self._return_iter_source_lines.splitlines()
+
+    def _iter_global(self, lines):
+        self.calls.append(
+                ('_iter_global', (lines,)))
+        try:
+            return self._return_iter_global.pop(0)
+        except IndexError:
+            return ('???', None)
+
+    def _iter_local(self, lines):
+        self.calls.append(
+                ('_iter_local', (lines,)))
+        try:
+            return self._return_iter_local.pop(0)
+        except IndexError:
+            return ('???', None)
+
+    def _parse_func(self, stmt, body):
+        self.calls.append(
+                ('_parse_func', (stmt, body)))
+        try:
+            return self._return_parse_func.pop(0)
+        except IndexError:
+            return ('???', '???')
+
+    def _parse_var(self, lines):
+        self.calls.append(
+                ('_parse_var', (lines,)))
+        try:
+            return self._return_parse_var.pop(0)
+        except IndexError:
+            return ('???', '???')
+
+    def _parse_compound(self, stmt, blocks):
+        self.calls.append(
+                ('_parse_compound', (stmt, blocks)))
+        try:
+            return self._return_parse_compound.pop(0)
+        except IndexError:
+            return (['???'], ['???'])
+
+    def test_empty_file(self):
+        self._return_iter_source_lines = ''
+        self._return_iter_global = [
+            [],
+            ]
+        self._return_parse_func = None
+        self._return_parse_var = None
+        self._return_parse_compound = None
+
+        srcvars = list(iter_variables('spam.c',
+                                      _iter_source_lines=self._iter_source_lines,
+                                      _iter_global=self._iter_global,
+                                      _iter_local=self._iter_local,
+                                      _parse_func=self._parse_func,
+                                      _parse_var=self._parse_var,
+                                      _parse_compound=self._parse_compound,
+                                      ))
+
+        self.assertEqual(srcvars, [])
+        self.assertEqual(self.calls, [
+            ('_iter_source_lines', ('spam.c',)),
+            ('_iter_global', ([],)),
+            ])
+
+    def test_no_statements(self):
+        content = textwrap.dedent('''
+        ...
+        ''')
+        self._return_iter_source_lines = content
+        self._return_iter_global = [
+            [],
+            ]
+        self._return_parse_func = None
+        self._return_parse_var = None
+        self._return_parse_compound = None
+
+        srcvars = list(iter_variables('spam.c',
+                                      _iter_source_lines=self._iter_source_lines,
+                                      _iter_global=self._iter_global,
+                                      _iter_local=self._iter_local,
+                                      _parse_func=self._parse_func,
+                                      _parse_var=self._parse_var,
+                                      _parse_compound=self._parse_compound,
+                                      ))
+
+        self.assertEqual(srcvars, [])
+        self.assertEqual(self.calls, [
+            ('_iter_source_lines', ('spam.c',)),
+            ('_iter_global', (content.splitlines(),)),
+            ])
+
+    def test_typical(self):
+        content = textwrap.dedent('''
+        ...
+        ''')
+        self._return_iter_source_lines = content
+        self._return_iter_global = [
+            [('<lines 1>', None),  # var1
+             ('<lines 2>', None),  # non-var
+             ('<lines 3>', None),  # var2
+             ('<lines 4>', '<body 1>'),  # func1
+             ('<lines 9>', None),  # var4
+             ],
+            ]
+        self._return_iter_local = [
+            # func1
+            [('<lines 5>', None),  # var3
+             ('<lines 6>', [('<header 1>', '<block 1>')]),  # if
+             ('<lines 8>', None),  # non-var
+             ],
+            # if
+            [('<lines 7>', None),  # var2 ("collision" with global var)
+             ],
+            ]
+        self._return_parse_func = [
+            ('func1', '<sig 1>'),
+            ]
+        self._return_parse_var = [
+            ('var1', '<vartype 1>'),
+            (None, None),
+            ('var2', '<vartype 2>'),
+            ('var3', '<vartype 3>'),
+            ('var2', '<vartype 2b>'),
+            ('var4', '<vartype 4>'),
+            (None, None),
+            (None, None),
+            (None, None),
+            ('var5', '<vartype 5>'),
+            ]
+        self._return_parse_compound = [
+            ([[
+                'if (',
+                '<simple>',
+                ')',
+                ],
+              ],
+             ['<block 1>']),
+            ]
+
+        srcvars = list(iter_variables('spam.c',
+                                      _iter_source_lines=self._iter_source_lines,
+                                      _iter_global=self._iter_global,
+                                      _iter_local=self._iter_local,
+                                      _parse_func=self._parse_func,
+                                      _parse_var=self._parse_var,
+                                      _parse_compound=self._parse_compound,
+                                      ))
+
+        self.assertEqual(srcvars, [
+            (None, 'var1', '<vartype 1>'),
+            (None, 'var2', '<vartype 2>'),
+            ('func1', 'var3', '<vartype 3>'),
+            ('func1', 'var2', '<vartype 2b>'),
+            ('func1', 'var4', '<vartype 4>'),
+            (None, 'var5', '<vartype 5>'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_iter_source_lines', ('spam.c',)),
+            ('_iter_global', (content.splitlines(),)),
+            ('_parse_var', ('<lines 1>',)),
+            ('_parse_var', ('<lines 2>',)),
+            ('_parse_var', ('<lines 3>',)),
+            ('_parse_func', ('<lines 4>', '<body 1>')),
+            ('_iter_local', (['<body 1>'],)),
+            ('_parse_var', ('<lines 5>',)),
+            ('_parse_compound', ('<lines 6>', [('<header 1>', '<block 1>')])),
+            ('_parse_var', ('if (',)),
+            ('_parse_var', ('<simple>',)),
+            ('_parse_var', (')',)),
+            ('_parse_var', ('<lines 8>',)),
+            ('_iter_local', (['<block 1>'],)),
+            ('_parse_var', ('<lines 7>',)),
+            ('_parse_var', ('<lines 9>',)),
+            ])
+
+    def test_no_locals(self):
+        content = textwrap.dedent('''
+        ...
+        ''')
+        self._return_iter_source_lines = content
+        self._return_iter_global = [
+            [('<lines 1>', None),  # var1
+             ('<lines 2>', None),  # non-var
+             ('<lines 3>', None),  # var2
+             ('<lines 4>', '<body 1>'),  # func1
+             ],
+            ]
+        self._return_iter_local = [
+            # func1
+            [('<lines 5>', None),  # non-var
+             ('<lines 6>', [('<header 1>', '<block 1>')]),  # if
+             ('<lines 8>', None),  # non-var
+             ],
+            # if
+            [('<lines 7>', None),  # non-var
+             ],
+            ]
+        self._return_parse_func = [
+            ('func1', '<sig 1>'),
+            ]
+        self._return_parse_var = [
+            ('var1', '<vartype 1>'),
+            (None, None),
+            ('var2', '<vartype 2>'),
+            (None, None),
+            (None, None),
+            (None, None),
+            (None, None),
+            (None, None),
+            (None, None),
+            ]
+        self._return_parse_compound = [
+            ([[
+                'if (',
+                '<simple>',
+                ')',
+                ],
+              ],
+             ['<block 1>']),
+            ]
+
+        srcvars = list(iter_variables('spam.c',
+                                      _iter_source_lines=self._iter_source_lines,
+                                      _iter_global=self._iter_global,
+                                      _iter_local=self._iter_local,
+                                      _parse_func=self._parse_func,
+                                      _parse_var=self._parse_var,
+                                      _parse_compound=self._parse_compound,
+                                      ))
+
+        self.assertEqual(srcvars, [
+            (None, 'var1', '<vartype 1>'),
+            (None, 'var2', '<vartype 2>'),
+            ])
+        self.assertEqual(self.calls, [
+            ('_iter_source_lines', ('spam.c',)),
+            ('_iter_global', (content.splitlines(),)),
+            ('_parse_var', ('<lines 1>',)),
+            ('_parse_var', ('<lines 2>',)),
+            ('_parse_var', ('<lines 3>',)),
+            ('_parse_func', ('<lines 4>', '<body 1>')),
+            ('_iter_local', (['<body 1>'],)),
+            ('_parse_var', ('<lines 5>',)),
+            ('_parse_compound', ('<lines 6>', [('<header 1>', '<block 1>')])),
+            ('_parse_var', ('if (',)),
+            ('_parse_var', ('<simple>',)),
+            ('_parse_var', (')',)),
+            ('_parse_var', ('<lines 8>',)),
+            ('_iter_local', (['<block 1>'],)),
+            ('_parse_var', ('<lines 7>',)),
+            ])
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_info.py
new file mode 100644 (file)
index 0000000..1dfe5d0
--- /dev/null
@@ -0,0 +1,208 @@
+import string
+import unittest
+
+from ..util import PseudoStr, StrProxy, Object
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common.info import ID
+    from c_parser.info import (
+        normalize_vartype, Variable,
+        )
+
+
+class NormalizeVartypeTests(unittest.TestCase):
+
+    def test_basic(self):
+        tests = [
+                (None, None),
+                ('', ''),
+                ('int', 'int'),
+                (PseudoStr('int'), 'int'),
+                (StrProxy('int'), 'int'),
+                ]
+        for vartype, expected in tests:
+            with self.subTest(vartype):
+                normalized = normalize_vartype(vartype)
+
+                self.assertEqual(normalized, expected)
+
+
+class VariableTests(unittest.TestCase):
+
+    VALID_ARGS = (
+            ('x/y/z/spam.c', 'func', 'eggs'),
+            'int',
+            )
+    VALID_KWARGS = dict(zip(Variable._fields, VALID_ARGS))
+    VALID_EXPECTED = VALID_ARGS
+
+    def test_init_typical_global(self):
+        static = Variable(
+                id=ID(
+                    filename='x/y/z/spam.c',
+                    funcname=None,
+                    name='eggs',
+                    ),
+                vartype='int',
+                )
+
+        self.assertEqual(static, (
+                ('x/y/z/spam.c', None, 'eggs'),
+                'int',
+                ))
+
+    def test_init_typical_local(self):
+        static = Variable(
+                id=ID(
+                    filename='x/y/z/spam.c',
+                    funcname='func',
+                    name='eggs',
+                    ),
+                vartype='int',
+                )
+
+        self.assertEqual(static, (
+                ('x/y/z/spam.c', 'func', 'eggs'),
+                'int',
+                ))
+
+    def test_init_all_missing(self):
+        for value in ('', None):
+            with self.subTest(repr(value)):
+                static = Variable(
+                        id=value,
+                        vartype=value,
+                        )
+
+                self.assertEqual(static, (
+                        None,
+                        None,
+                        ))
+
+    def test_init_all_coerced(self):
+        id = ID('x/y/z/spam.c', 'func', 'spam')
+        tests = [
+            ('str subclass',
+             dict(
+                 id=(
+                    PseudoStr('x/y/z/spam.c'),
+                    PseudoStr('func'),
+                    PseudoStr('spam'),
+                    ),
+                 vartype=PseudoStr('int'),
+                 ),
+             (id,
+              'int',
+              )),
+            ('non-str 1',
+             dict(
+                 id=id,
+                 vartype=Object(),
+                 ),
+             (id,
+              '<object>',
+              )),
+            ('non-str 2',
+             dict(
+                 id=id,
+                 vartype=StrProxy('variable'),
+                 ),
+             (id,
+              'variable',
+              )),
+            ('non-str',
+             dict(
+                 id=id,
+                 vartype=('a', 'b', 'c'),
+                 ),
+             (id,
+              "('a', 'b', 'c')",
+              )),
+            ]
+        for summary, kwargs, expected in tests:
+            with self.subTest(summary):
+                static = Variable(**kwargs)
+
+                for field in Variable._fields:
+                    value = getattr(static, field)
+                    if field == 'id':
+                        self.assertIs(type(value), ID)
+                    else:
+                        self.assertIs(type(value), str)
+                self.assertEqual(tuple(static), expected)
+
+    def test_iterable(self):
+        static = Variable(**self.VALID_KWARGS)
+
+        id, vartype = static
+
+        values = (id, vartype)
+        for value, expected in zip(values, self.VALID_EXPECTED):
+            self.assertEqual(value, expected)
+
+    def test_fields(self):
+        static = Variable(('a', 'b', 'z'), 'x')
+
+        self.assertEqual(static.id, ('a', 'b', 'z'))
+        self.assertEqual(static.vartype, 'x')
+
+    def test___getattr__(self):
+        static = Variable(('a', 'b', 'z'), 'x')
+
+        self.assertEqual(static.filename, 'a')
+        self.assertEqual(static.funcname, 'b')
+        self.assertEqual(static.name, 'z')
+
+    def test_validate_typical(self):
+        static = Variable(
+                id=ID(
+                    filename='x/y/z/spam.c',
+                    funcname='func',
+                    name='eggs',
+                    ),
+                vartype='int',
+                )
+
+        static.validate()  # This does not fail.
+
+    def test_validate_missing_field(self):
+        for field in Variable._fields:
+            with self.subTest(field):
+                static = Variable(**self.VALID_KWARGS)
+                static = static._replace(**{field: None})
+
+                with self.assertRaises(TypeError):
+                    static.validate()
+
+    def test_validate_bad_field(self):
+        badch = tuple(c for c in string.punctuation + string.digits)
+        notnames = (
+                '1a',
+                'a.b',
+                'a-b',
+                '&a',
+                'a++',
+                ) + badch
+        tests = [
+            ('id', ()),  # Any non-empty str is okay.
+            ('vartype', ()),  # Any non-empty str is okay.
+            ]
+        seen = set()
+        for field, invalid in tests:
+            for value in invalid:
+                seen.add(value)
+                with self.subTest(f'{field}={value!r}'):
+                    static = Variable(**self.VALID_KWARGS)
+                    static = static._replace(**{field: value})
+
+                    with self.assertRaises(ValueError):
+                        static.validate()
+
+        for field, invalid in tests:
+            valid = seen - set(invalid)
+            for value in valid:
+                with self.subTest(f'{field}={value!r}'):
+                    static = Variable(**self.VALID_KWARGS)
+                    static = static._replace(**{field: value})
+
+                    static.validate()  # This does not fail.
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_preprocessor.py b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_preprocessor.py
new file mode 100644 (file)
index 0000000..89e1557
--- /dev/null
@@ -0,0 +1,1562 @@
+import itertools
+import textwrap
+import unittest
+import sys
+
+from ..util import wrapped_arg_combos, StrProxy
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_parser.preprocessor import (
+        iter_lines,
+        # directives
+        parse_directive, PreprocessorDirective,
+        Constant, Macro, IfDirective, Include, OtherDirective,
+        )
+
+
+class TestCaseBase(unittest.TestCase):
+
+    maxDiff = None
+
+    def reset(self):
+        self._calls = []
+        self.errors = None
+
+    @property
+    def calls(self):
+        try:
+            return self._calls
+        except AttributeError:
+            self._calls = []
+            return self._calls
+
+    errors = None
+
+    def try_next_exc(self):
+        if not self.errors:
+            return
+        if exc := self.errors.pop(0):
+            raise exc
+
+    def check_calls(self, *expected):
+        self.assertEqual(self.calls, list(expected))
+        self.assertEqual(self.errors or [], [])
+
+
+class IterLinesTests(TestCaseBase):
+
+    parsed = None
+
+    def check_calls(self, *expected):
+        super().check_calls(*expected)
+        self.assertEqual(self.parsed or [], [])
+
+    def _parse_directive(self, line):
+        self.calls.append(
+                ('_parse_directive', line))
+        self.try_next_exc()
+        return self.parsed.pop(0)
+
+    def test_no_lines(self):
+        lines = []
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [])
+        self.check_calls()
+
+    def test_no_directives(self):
+        lines = textwrap.dedent('''
+
+            // xyz
+            typedef enum {
+                SPAM
+                EGGS
+            } kind;
+
+            struct info {
+                kind kind;
+                int status;
+            };
+
+            typedef struct spam {
+                struct info info;
+            } myspam;
+
+            static int spam = 0;
+
+            /**
+             * ...
+             */
+            static char *
+            get_name(int arg,
+                     char *default,
+                     )
+            {
+                return default
+            }
+
+            int check(void) {
+                return 0;
+            }
+
+            ''')[1:-1].splitlines()
+        expected = [(lno, line, None, ())
+                    for lno, line in enumerate(lines, 1)]
+        expected[1] = (2, ' ', None, ())
+        expected[20] = (21, ' ', None, ())
+        del expected[19]
+        del expected[18]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, expected)
+        self.check_calls()
+
+    def test_single_directives(self):
+        tests = [
+            ('#include <stdio>', Include('<stdio>')),
+            ('#define SPAM 1', Constant('SPAM', '1')),
+            ('#define SPAM() 1', Macro('SPAM', (), '1')),
+            ('#define SPAM(a, b) a = b;', Macro('SPAM', ('a', 'b'), 'a = b;')),
+            ('#if defined(SPAM)', IfDirective('if', 'defined(SPAM)')),
+            ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')),
+            ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')),
+            ('#elseif defined(SPAM)', IfDirective('elseif', 'defined(SPAM)')),
+            ('#else', OtherDirective('else', None)),
+            ('#endif', OtherDirective('endif', None)),
+            ('#error ...', OtherDirective('error', '...')),
+            ('#warning ...', OtherDirective('warning', '...')),
+            ('#__FILE__ ...', OtherDirective('__FILE__', '...')),
+            ('#__LINE__ ...', OtherDirective('__LINE__', '...')),
+            ('#__DATE__ ...', OtherDirective('__DATE__', '...')),
+            ('#__TIME__ ...', OtherDirective('__TIME__', '...')),
+            ('#__TIMESTAMP__ ...', OtherDirective('__TIMESTAMP__', '...')),
+            ]
+        for line, directive in tests:
+            with self.subTest(line):
+                self.reset()
+                self.parsed = [
+                    directive,
+                    ]
+                text = textwrap.dedent('''
+                    static int spam = 0;
+                    {}
+                    static char buffer[256];
+                    ''').strip().format(line)
+                lines = text.strip().splitlines()
+
+                results = list(
+                        iter_lines(lines, _parse_directive=self._parse_directive))
+
+                self.assertEqual(results, [
+                    (1, 'static int spam = 0;', None, ()),
+                    (2, line, directive, ()),
+                    ((3, 'static char buffer[256];', None, ('defined(SPAM)',))
+                     if directive.kind in ('if', 'ifdef', 'elseif')
+                     else (3, 'static char buffer[256];', None, ('! defined(SPAM)',))
+                     if directive.kind == 'ifndef'
+                     else (3, 'static char buffer[256];', None, ())),
+                    ])
+                self.check_calls(
+                        ('_parse_directive', line),
+                        )
+
+    def test_directive_whitespace(self):
+        line = ' # define  eggs  (  a  ,  b  )  {  a  =  b  ;  }  '
+        directive = Macro('eggs', ('a', 'b'), '{ a = b; }')
+        self.parsed = [
+            directive,
+            ]
+        lines = [line]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, line, directive, ()),
+            ])
+        self.check_calls(
+                ('_parse_directive', '#define eggs ( a , b ) { a = b ; }'),
+                )
+
+    @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows')
+    def test_split_lines(self):
+        directive = Macro('eggs', ('a', 'b'), '{ a = b; }')
+        self.parsed = [
+            directive,
+            ]
+        text = textwrap.dedent(r'''
+            static int spam = 0;
+            #define eggs(a, b) \
+                { \
+                    a = b; \
+                }
+            static char buffer[256];
+            ''').strip()
+        lines = [line + '\n' for line in text.splitlines()]
+        lines[-1] = lines[-1][:-1]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, 'static int spam = 0;\n', None, ()),
+            (5, '#define eggs(a, b)      {          a = b;      }\n', directive, ()),
+            (6, 'static char buffer[256];', None, ()),
+            ])
+        self.check_calls(
+                ('_parse_directive', '#define eggs(a, b) { a = b; }'),
+                )
+
+    def test_nested_conditions(self):
+        directives = [
+            IfDirective('ifdef', 'SPAM'),
+            IfDirective('if', 'SPAM == 1'),
+            IfDirective('elseif', 'SPAM == 2'),
+            OtherDirective('else', None),
+            OtherDirective('endif', None),
+            OtherDirective('endif', None),
+            ]
+        self.parsed = list(directives)
+        text = textwrap.dedent(r'''
+            static int spam = 0;
+
+            #ifdef SPAM
+            static int start = 0;
+            #  if SPAM == 1
+            static char buffer[10];
+            #  elif SPAM == 2
+            static char buffer[100];
+            #  else
+            static char buffer[256];
+            #  endif
+            static int end = 0;
+            #endif
+
+            static int eggs = 0;
+            ''').strip()
+        lines = [line for line in text.splitlines() if line.strip()]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, 'static int spam = 0;', None, ()),
+            (2, '#ifdef SPAM', directives[0], ()),
+            (3, 'static int start = 0;', None, ('defined(SPAM)',)),
+            (4, '#  if SPAM == 1', directives[1], ('defined(SPAM)',)),
+            (5, 'static char buffer[10];', None, ('defined(SPAM)', 'SPAM == 1')),
+            (6, '#  elif SPAM == 2', directives[2], ('defined(SPAM)', 'SPAM == 1')),
+            (7, 'static char buffer[100];', None, ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')),
+            (8, '#  else', directives[3], ('defined(SPAM)', '! (SPAM == 1)', 'SPAM == 2')),
+            (9, 'static char buffer[256];', None, ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')),
+            (10, '#  endif', directives[4], ('defined(SPAM)', '! (SPAM == 1)', '! (SPAM == 2)')),
+            (11, 'static int end = 0;', None, ('defined(SPAM)',)),
+            (12, '#endif', directives[5], ('defined(SPAM)',)),
+            (13, 'static int eggs = 0;', None, ()),
+            ])
+        self.check_calls(
+                ('_parse_directive', '#ifdef SPAM'),
+                ('_parse_directive', '#if SPAM == 1'),
+                ('_parse_directive', '#elif SPAM == 2'),
+                ('_parse_directive', '#else'),
+                ('_parse_directive', '#endif'),
+                ('_parse_directive', '#endif'),
+                )
+
+    def test_split_blocks(self):
+        directives = [
+            IfDirective('ifdef', 'SPAM'),
+            OtherDirective('else', None),
+            OtherDirective('endif', None),
+            ]
+        self.parsed = list(directives)
+        text = textwrap.dedent(r'''
+            void str_copy(char *buffer, *orig);
+
+            int init(char *name) {
+                static int initialized = 0;
+                if (initialized) {
+                    return 0;
+                }
+            #ifdef SPAM
+                static char buffer[10];
+                str_copy(buffer, char);
+            }
+
+            void copy(char *buffer, *orig) {
+                strncpy(buffer, orig, 9);
+                buffer[9] = 0;
+            }
+
+            #else
+                static char buffer[256];
+                str_copy(buffer, char);
+            }
+
+            void copy(char *buffer, *orig) {
+                strcpy(buffer, orig);
+            }
+
+            #endif
+            ''').strip()
+        lines = [line for line in text.splitlines() if line.strip()]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, 'void str_copy(char *buffer, *orig);', None, ()),
+            (2, 'int init(char *name) {', None, ()),
+            (3, '    static int initialized = 0;', None, ()),
+            (4, '    if (initialized) {', None, ()),
+            (5, '        return 0;', None, ()),
+            (6, '    }', None, ()),
+
+            (7, '#ifdef SPAM', directives[0], ()),
+
+            (8, '    static char buffer[10];', None, ('defined(SPAM)',)),
+            (9, '    str_copy(buffer, char);', None, ('defined(SPAM)',)),
+            (10, '}', None, ('defined(SPAM)',)),
+            (11, 'void copy(char *buffer, *orig) {', None, ('defined(SPAM)',)),
+            (12, '    strncpy(buffer, orig, 9);', None, ('defined(SPAM)',)),
+            (13, '    buffer[9] = 0;', None, ('defined(SPAM)',)),
+            (14, '}', None, ('defined(SPAM)',)),
+
+            (15, '#else', directives[1], ('defined(SPAM)',)),
+
+            (16, '    static char buffer[256];', None, ('! (defined(SPAM))',)),
+            (17, '    str_copy(buffer, char);', None, ('! (defined(SPAM))',)),
+            (18, '}', None, ('! (defined(SPAM))',)),
+            (19, 'void copy(char *buffer, *orig) {', None, ('! (defined(SPAM))',)),
+            (20, '    strcpy(buffer, orig);', None, ('! (defined(SPAM))',)),
+            (21, '}', None, ('! (defined(SPAM))',)),
+
+            (22, '#endif', directives[2], ('! (defined(SPAM))',)),
+            ])
+        self.check_calls(
+                ('_parse_directive', '#ifdef SPAM'),
+                ('_parse_directive', '#else'),
+                ('_parse_directive', '#endif'),
+                )
+
+    @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows')
+    def test_basic(self):
+        directives = [
+            Include('<stdio.h>'),
+            IfDirective('ifdef', 'SPAM'),
+            IfDirective('if', '! defined(HAM) || !HAM'),
+            Constant('HAM', '0'),
+            IfDirective('elseif', 'HAM < 0'),
+            Constant('HAM', '-1'),
+            OtherDirective('else', None),
+            OtherDirective('endif', None),
+            OtherDirective('endif', None),
+            IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'),
+            OtherDirective('undef', 'HAM'),
+            OtherDirective('endif', None),
+            IfDirective('ifndef', 'HAM'),
+            OtherDirective('endif', None),
+            ]
+        self.parsed = list(directives)
+        text = textwrap.dedent(r'''
+            #include <stdio.h>
+            print("begin");
+            #ifdef SPAM
+               print("spam");
+               #if ! defined(HAM) || !HAM
+            #      DEFINE HAM 0
+               #elseif HAM < 0
+            #      DEFINE HAM -1
+               #else
+                   print("ham HAM");
+               #endif
+            #endif
+
+            #if defined(HAM) && \
+                (HAM < 0 || ! HAM)
+              print("ham?");
+              #undef HAM
+            # endif
+
+            #ifndef HAM
+               print("no ham");
+            #endif
+            print("end");
+            ''')[1:-1]
+        lines = [line + '\n' for line in text.splitlines()]
+        lines[-1] = lines[-1][:-1]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, '#include <stdio.h>\n', Include('<stdio.h>'), ()),
+            (2, 'print("begin");\n', None, ()),
+            #
+            (3, '#ifdef SPAM\n',
+                IfDirective('ifdef', 'SPAM'),
+                ()),
+            (4, '   print("spam");\n',
+                None,
+                ('defined(SPAM)',)),
+            (5, '   #if ! defined(HAM) || !HAM\n',
+                IfDirective('if', '! defined(HAM) || !HAM'),
+                ('defined(SPAM)',)),
+            (6, '#      DEFINE HAM 0\n',
+                Constant('HAM', '0'),
+                ('defined(SPAM)', '! defined(HAM) || !HAM')),
+            (7, '   #elseif HAM < 0\n',
+                IfDirective('elseif', 'HAM < 0'),
+                ('defined(SPAM)', '! defined(HAM) || !HAM')),
+            (8, '#      DEFINE HAM -1\n',
+                Constant('HAM', '-1'),
+                ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')),
+            (9, '   #else\n',
+                OtherDirective('else', None),
+                ('defined(SPAM)', '! (! defined(HAM) || !HAM)', 'HAM < 0')),
+            (10, '       print("ham HAM");\n',
+                None,
+                ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')),
+            (11, '   #endif\n',
+                OtherDirective('endif', None),
+                ('defined(SPAM)', '! (! defined(HAM) || !HAM)', '! (HAM < 0)')),
+            (12, '#endif\n',
+                OtherDirective('endif', None),
+                ('defined(SPAM)',)),
+            #
+            (13, '\n', None, ()),
+            #
+            (15, '#if defined(HAM) &&      (HAM < 0 || ! HAM)\n',
+                IfDirective('if', 'defined(HAM) && (HAM < 0 || ! HAM)'),
+                ()),
+            (16, '  print("ham?");\n',
+                None,
+                ('defined(HAM) && (HAM < 0 || ! HAM)',)),
+            (17, '  #undef HAM\n',
+                OtherDirective('undef', 'HAM'),
+                ('defined(HAM) && (HAM < 0 || ! HAM)',)),
+            (18, '# endif\n',
+                OtherDirective('endif', None),
+                ('defined(HAM) && (HAM < 0 || ! HAM)',)),
+            #
+            (19, '\n', None, ()),
+            #
+            (20, '#ifndef HAM\n',
+                IfDirective('ifndef', 'HAM'),
+                ()),
+            (21, '   print("no ham");\n',
+                None,
+                ('! defined(HAM)',)),
+            (22, '#endif\n',
+                OtherDirective('endif', None),
+                ('! defined(HAM)',)),
+            #
+            (23, 'print("end");', None, ()),
+            ])
+
+    @unittest.skipIf(sys.platform == 'win32', 'needs fix under Windows')
+    def test_typical(self):
+        # We use Include/compile.h from commit 66c4f3f38b86.  It has
+        # a good enough mix of code without being too large.
+        directives = [
+            IfDirective('ifndef', 'Py_COMPILE_H'),
+            Constant('Py_COMPILE_H', None),
+
+            IfDirective('ifndef', 'Py_LIMITED_API'),
+
+            Include('"code.h"'),
+
+            IfDirective('ifdef', '__cplusplus'),
+            OtherDirective('endif', None),
+
+            Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'),
+            Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'),
+            Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'),
+            Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'),
+            Constant('PyCF_ONLY_AST', '0x0400'),
+            Constant('PyCF_IGNORE_COOKIE', '0x0800'),
+            Constant('PyCF_TYPE_COMMENTS', '0x1000'),
+            Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'),
+
+            IfDirective('ifndef', 'Py_LIMITED_API'),
+            OtherDirective('endif', None),
+
+            Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'),
+            Constant('FUTURE_GENERATORS', '"generators"'),
+            Constant('FUTURE_DIVISION', '"division"'),
+            Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'),
+            Constant('FUTURE_WITH_STATEMENT', '"with_statement"'),
+            Constant('FUTURE_PRINT_FUNCTION', '"print_function"'),
+            Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'),
+            Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'),
+            Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'),
+            Constant('FUTURE_ANNOTATIONS', '"annotations"'),
+
+            Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'),
+
+            Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'),
+
+            IfDirective('ifdef', '__cplusplus'),
+            OtherDirective('endif', None),
+
+            OtherDirective('endif', None),  # ifndef Py_LIMITED_API
+
+            Constant('Py_single_input', '256'),
+            Constant('Py_file_input', '257'),
+            Constant('Py_eval_input', '258'),
+            Constant('Py_func_type_input', '345'),
+
+            OtherDirective('endif', None),  # ifndef Py_COMPILE_H
+            ]
+        self.parsed = list(directives)
+        text = textwrap.dedent(r'''
+            #ifndef Py_COMPILE_H
+            #define Py_COMPILE_H
+
+            #ifndef Py_LIMITED_API
+            #include "code.h"
+
+            #ifdef __cplusplus
+            extern "C" {
+            #endif
+
+            /* Public interface */
+            struct _node; /* Declare the existence of this type */
+            PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);
+            /* XXX (ncoghlan): Unprefixed type name in a public API! */
+
+            #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \
+                               CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \
+                               CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \
+                               CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)
+            #define PyCF_MASK_OBSOLETE (CO_NESTED)
+            #define PyCF_SOURCE_IS_UTF8  0x0100
+            #define PyCF_DONT_IMPLY_DEDENT 0x0200
+            #define PyCF_ONLY_AST 0x0400
+            #define PyCF_IGNORE_COOKIE 0x0800
+            #define PyCF_TYPE_COMMENTS 0x1000
+            #define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000
+
+            #ifndef Py_LIMITED_API
+            typedef struct {
+                int cf_flags;  /* bitmask of CO_xxx flags relevant to future */
+                int cf_feature_version;  /* minor Python version (PyCF_ONLY_AST) */
+            } PyCompilerFlags;
+            #endif
+
+            /* Future feature support */
+
+            typedef struct {
+                int ff_features;      /* flags set by future statements */
+                int ff_lineno;        /* line number of last future statement */
+            } PyFutureFeatures;
+
+            #define FUTURE_NESTED_SCOPES "nested_scopes"
+            #define FUTURE_GENERATORS "generators"
+            #define FUTURE_DIVISION "division"
+            #define FUTURE_ABSOLUTE_IMPORT "absolute_import"
+            #define FUTURE_WITH_STATEMENT "with_statement"
+            #define FUTURE_PRINT_FUNCTION "print_function"
+            #define FUTURE_UNICODE_LITERALS "unicode_literals"
+            #define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"
+            #define FUTURE_GENERATOR_STOP "generator_stop"
+            #define FUTURE_ANNOTATIONS "annotations"
+
+            struct _mod; /* Declare the existence of this type */
+            #define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)
+            PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(
+                struct _mod *mod,
+                const char *filename,       /* decoded from the filesystem encoding */
+                PyCompilerFlags *flags,
+                int optimize,
+                PyArena *arena);
+            PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject(
+                struct _mod *mod,
+                PyObject *filename,
+                PyCompilerFlags *flags,
+                int optimize,
+                PyArena *arena);
+            PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(
+                struct _mod * mod,
+                const char *filename        /* decoded from the filesystem encoding */
+                );
+            PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(
+                struct _mod * mod,
+                PyObject *filename
+                );
+
+            /* _Py_Mangle is defined in compile.c */
+            PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);
+
+            #define PY_INVALID_STACK_EFFECT INT_MAX
+            PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);
+            PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);
+
+            PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize);
+
+            #ifdef __cplusplus
+            }
+            #endif
+
+            #endif /* !Py_LIMITED_API */
+
+            /* These definitions must match corresponding definitions in graminit.h. */
+            #define Py_single_input 256
+            #define Py_file_input 257
+            #define Py_eval_input 258
+            #define Py_func_type_input 345
+
+            #endif /* !Py_COMPILE_H */
+            ''').strip()
+        lines = [line + '\n' for line in text.splitlines()]
+        lines[-1] = lines[-1][:-1]
+
+        results = list(
+                iter_lines(lines, _parse_directive=self._parse_directive))
+
+        self.assertEqual(results, [
+            (1, '#ifndef Py_COMPILE_H\n',
+                IfDirective('ifndef', 'Py_COMPILE_H'),
+                ()),
+            (2, '#define Py_COMPILE_H\n',
+                Constant('Py_COMPILE_H', None),
+                ('! defined(Py_COMPILE_H)',)),
+            (3, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)',)),
+            (4, '#ifndef Py_LIMITED_API\n',
+                IfDirective('ifndef', 'Py_LIMITED_API'),
+                ('! defined(Py_COMPILE_H)',)),
+            (5, '#include "code.h"\n',
+                Include('"code.h"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (6, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (7, '#ifdef __cplusplus\n',
+                IfDirective('ifdef', '__cplusplus'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (8, 'extern "C" {\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')),
+            (9, '#endif\n',
+                OtherDirective('endif', None),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')),
+            (10, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (11, ' \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (12, 'struct _node;  \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (13, 'PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (14, ' \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (15, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (19, '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT |                     CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION |                     CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL |                     CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)\n',
+                Constant('PyCF_MASK', '(CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (20, '#define PyCF_MASK_OBSOLETE (CO_NESTED)\n',
+                Constant('PyCF_MASK_OBSOLETE', '(CO_NESTED)'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (21, '#define PyCF_SOURCE_IS_UTF8  0x0100\n',
+                Constant('PyCF_SOURCE_IS_UTF8', ' 0x0100'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (22, '#define PyCF_DONT_IMPLY_DEDENT 0x0200\n',
+                Constant('PyCF_DONT_IMPLY_DEDENT', '0x0200'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (23, '#define PyCF_ONLY_AST 0x0400\n',
+                Constant('PyCF_ONLY_AST', '0x0400'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (24, '#define PyCF_IGNORE_COOKIE 0x0800\n',
+                Constant('PyCF_IGNORE_COOKIE', '0x0800'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (25, '#define PyCF_TYPE_COMMENTS 0x1000\n',
+                Constant('PyCF_TYPE_COMMENTS', '0x1000'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (26, '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000\n',
+                Constant('PyCF_ALLOW_TOP_LEVEL_AWAIT', '0x2000'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (27, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (28, '#ifndef Py_LIMITED_API\n',
+                IfDirective('ifndef', 'Py_LIMITED_API'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (29, 'typedef struct {\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')),
+            (30, '    int cf_flags;   \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')),
+            (31, '    int cf_feature_version;   \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')),
+            (32, '} PyCompilerFlags;\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')),
+            (33, '#endif\n',
+                OtherDirective('endif', None),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', '! defined(Py_LIMITED_API)')),
+            (34, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (35, ' \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (36, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (37, 'typedef struct {\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (38, '    int ff_features;       \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (39, '    int ff_lineno;         \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (40, '} PyFutureFeatures;\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (41, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (42, '#define FUTURE_NESTED_SCOPES "nested_scopes"\n',
+                Constant('FUTURE_NESTED_SCOPES', '"nested_scopes"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (43, '#define FUTURE_GENERATORS "generators"\n',
+                Constant('FUTURE_GENERATORS', '"generators"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (44, '#define FUTURE_DIVISION "division"\n',
+                Constant('FUTURE_DIVISION', '"division"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (45, '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"\n',
+                Constant('FUTURE_ABSOLUTE_IMPORT', '"absolute_import"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (46, '#define FUTURE_WITH_STATEMENT "with_statement"\n',
+                Constant('FUTURE_WITH_STATEMENT', '"with_statement"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (47, '#define FUTURE_PRINT_FUNCTION "print_function"\n',
+                Constant('FUTURE_PRINT_FUNCTION', '"print_function"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (48, '#define FUTURE_UNICODE_LITERALS "unicode_literals"\n',
+                Constant('FUTURE_UNICODE_LITERALS', '"unicode_literals"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (49, '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"\n',
+                Constant('FUTURE_BARRY_AS_BDFL', '"barry_as_FLUFL"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (50, '#define FUTURE_GENERATOR_STOP "generator_stop"\n',
+                Constant('FUTURE_GENERATOR_STOP', '"generator_stop"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (51, '#define FUTURE_ANNOTATIONS "annotations"\n',
+                Constant('FUTURE_ANNOTATIONS', '"annotations"'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (52, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (53, 'struct _mod;  \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (54, '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)\n',
+                Macro('PyAST_Compile', ('mod', 's', 'f', 'ar'), 'PyAST_CompileEx(mod, s, f, -1, ar)'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (55, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileEx(\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (56, '    struct _mod *mod,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (57, '    const char *filename,        \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (58, '    PyCompilerFlags *flags,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (59, '    int optimize,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (60, '    PyArena *arena);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (61, 'PyAPI_FUNC(PyCodeObject *) PyAST_CompileObject(\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (62, '    struct _mod *mod,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (63, '    PyObject *filename,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (64, '    PyCompilerFlags *flags,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (65, '    int optimize,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (66, '    PyArena *arena);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (67, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromAST(\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (68, '    struct _mod * mod,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (69, '    const char *filename         \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (70, '    );\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (71, 'PyAPI_FUNC(PyFutureFeatures *) PyFuture_FromASTObject(\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (72, '    struct _mod * mod,\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (73, '    PyObject *filename\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (74, '    );\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (75, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (76, ' \n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (77, 'PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (78, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (79, '#define PY_INVALID_STACK_EFFECT INT_MAX\n',
+                Constant('PY_INVALID_STACK_EFFECT', 'INT_MAX'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (80, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffect(int opcode, int oparg);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (81, 'PyAPI_FUNC(int) PyCompile_OpcodeStackEffectWithJump(int opcode, int oparg, int jump);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (82, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (83, 'PyAPI_FUNC(int) _PyAST_Optimize(struct _mod *, PyArena *arena, int optimize);\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (84, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (85, '#ifdef __cplusplus\n',
+                IfDirective('ifdef', '__cplusplus'),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (86, '}\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')),
+            (87, '#endif\n',
+                OtherDirective('endif', None),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)', 'defined(__cplusplus)')),
+            (88, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (89, '#endif  \n',
+                OtherDirective('endif', None),
+                ('! defined(Py_COMPILE_H)', '! defined(Py_LIMITED_API)')),
+            (90, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)',)),
+            (91, ' \n',
+                None,
+                ('! defined(Py_COMPILE_H)',)),
+            (92, '#define Py_single_input 256\n',
+                Constant('Py_single_input', '256'),
+                ('! defined(Py_COMPILE_H)',)),
+            (93, '#define Py_file_input 257\n',
+                Constant('Py_file_input', '257'),
+                ('! defined(Py_COMPILE_H)',)),
+            (94, '#define Py_eval_input 258\n',
+                Constant('Py_eval_input', '258'),
+                ('! defined(Py_COMPILE_H)',)),
+            (95, '#define Py_func_type_input 345\n',
+                Constant('Py_func_type_input', '345'),
+                ('! defined(Py_COMPILE_H)',)),
+            (96, '\n',
+                None,
+                ('! defined(Py_COMPILE_H)',)),
+            (97, '#endif  ',
+                OtherDirective('endif', None),
+                ('! defined(Py_COMPILE_H)',)),
+            ])
+        self.check_calls(
+                ('_parse_directive', '#ifndef Py_COMPILE_H'),
+                ('_parse_directive', '#define Py_COMPILE_H'),
+                ('_parse_directive', '#ifndef Py_LIMITED_API'),
+                ('_parse_directive', '#include "code.h"'),
+                ('_parse_directive', '#ifdef __cplusplus'),
+                ('_parse_directive', '#endif'),
+                ('_parse_directive', '#define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)'),
+                ('_parse_directive', '#define PyCF_MASK_OBSOLETE (CO_NESTED)'),
+                ('_parse_directive', '#define PyCF_SOURCE_IS_UTF8 0x0100'),
+                ('_parse_directive', '#define PyCF_DONT_IMPLY_DEDENT 0x0200'),
+                ('_parse_directive', '#define PyCF_ONLY_AST 0x0400'),
+                ('_parse_directive', '#define PyCF_IGNORE_COOKIE 0x0800'),
+                ('_parse_directive', '#define PyCF_TYPE_COMMENTS 0x1000'),
+                ('_parse_directive', '#define PyCF_ALLOW_TOP_LEVEL_AWAIT 0x2000'),
+                ('_parse_directive', '#ifndef Py_LIMITED_API'),
+                ('_parse_directive', '#endif'),
+                ('_parse_directive', '#define FUTURE_NESTED_SCOPES "nested_scopes"'),
+                ('_parse_directive', '#define FUTURE_GENERATORS "generators"'),
+                ('_parse_directive', '#define FUTURE_DIVISION "division"'),
+                ('_parse_directive', '#define FUTURE_ABSOLUTE_IMPORT "absolute_import"'),
+                ('_parse_directive', '#define FUTURE_WITH_STATEMENT "with_statement"'),
+                ('_parse_directive', '#define FUTURE_PRINT_FUNCTION "print_function"'),
+                ('_parse_directive', '#define FUTURE_UNICODE_LITERALS "unicode_literals"'),
+                ('_parse_directive', '#define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"'),
+                ('_parse_directive', '#define FUTURE_GENERATOR_STOP "generator_stop"'),
+                ('_parse_directive', '#define FUTURE_ANNOTATIONS "annotations"'),
+                ('_parse_directive', '#define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)'),
+                ('_parse_directive', '#define PY_INVALID_STACK_EFFECT INT_MAX'),
+                ('_parse_directive', '#ifdef __cplusplus'),
+                ('_parse_directive', '#endif'),
+                ('_parse_directive', '#endif'),
+                ('_parse_directive', '#define Py_single_input 256'),
+                ('_parse_directive', '#define Py_file_input 257'),
+                ('_parse_directive', '#define Py_eval_input 258'),
+                ('_parse_directive', '#define Py_func_type_input 345'),
+                ('_parse_directive', '#endif'),
+                )
+
+
+class ParseDirectiveTests(unittest.TestCase):
+
+    def test_directives(self):
+        tests = [
+            # includes
+            ('#include "internal/pycore_pystate.h"', Include('"internal/pycore_pystate.h"')),
+            ('#include <stdio>', Include('<stdio>')),
+
+            # defines
+            ('#define SPAM int', Constant('SPAM', 'int')),
+            ('#define SPAM', Constant('SPAM', '')),
+            ('#define SPAM(x, y) run(x, y)', Macro('SPAM', ('x', 'y'), 'run(x, y)')),
+            ('#undef SPAM', None),
+
+            # conditionals
+            ('#if SPAM', IfDirective('if', 'SPAM')),
+            # XXX complex conditionls
+            ('#ifdef SPAM', IfDirective('ifdef', 'SPAM')),
+            ('#ifndef SPAM', IfDirective('ifndef', 'SPAM')),
+            ('#elseif SPAM', IfDirective('elseif', 'SPAM')),
+            # XXX complex conditionls
+            ('#else', OtherDirective('else', '')),
+            ('#endif', OtherDirective('endif', '')),
+
+            # other
+            ('#error oops!', None),
+            ('#warning oops!', None),
+            ('#pragma ...', None),
+            ('#__FILE__ ...', None),
+            ('#__LINE__ ...', None),
+            ('#__DATE__ ...', None),
+            ('#__TIME__ ...', None),
+            ('#__TIMESTAMP__ ...', None),
+
+            # extra whitespace
+            (' # include  <stdio> ', Include('<stdio>')),
+            ('#else  ', OtherDirective('else', '')),
+            ('#endif  ', OtherDirective('endif', '')),
+            ('#define SPAM int  ', Constant('SPAM', 'int')),
+            ('#define SPAM  ', Constant('SPAM', '')),
+            ]
+        for line, expected in tests:
+            if expected is None:
+                kind, _, text = line[1:].partition(' ')
+                expected = OtherDirective(kind, text)
+            with self.subTest(line):
+                directive = parse_directive(line)
+
+                self.assertEqual(directive, expected)
+
+    def test_bad_directives(self):
+        tests = [
+            # valid directives with bad text
+            '#define 123',
+            '#else spam',
+            '#endif spam',
+            ]
+        for kind in PreprocessorDirective.KINDS:
+            # missing leading "#"
+            tests.append(kind)
+            if kind in ('else', 'endif'):
+                continue
+            # valid directives with missing text
+            tests.append('#' + kind)
+            tests.append('#' + kind + ' ')
+        for line in tests:
+            with self.subTest(line):
+                with self.assertRaises(ValueError):
+                    parse_directive(line)
+
+    def test_not_directives(self):
+        tests = [
+            '',
+            ' ',
+            'directive',
+            'directive?',
+            '???',
+            ]
+        for line in tests:
+            with self.subTest(line):
+                with self.assertRaises(ValueError):
+                    parse_directive(line)
+
+
+class ConstantTests(unittest.TestCase):
+
+    def test_type(self):
+        directive = Constant('SPAM', '123')
+
+        self.assertIs(type(directive), Constant)
+        self.assertIsInstance(directive, PreprocessorDirective)
+
+    def test_attrs(self):
+        d = Constant('SPAM', '123')
+        kind, name, value = d.kind, d.name, d.value
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertEqual(value, '123')
+
+    def test_text(self):
+        tests = [
+            (('SPAM', '123'), 'SPAM 123'),
+            (('SPAM',), 'SPAM'),
+            ]
+        for args, expected in tests:
+            with self.subTest(args):
+                d = Constant(*args)
+                text = d.text
+
+                self.assertEqual(text, expected)
+
+    def test_iter(self):
+        kind, name, value = Constant('SPAM', '123')
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertEqual(value, '123')
+
+    def test_defaults(self):
+        kind, name, value = Constant('SPAM')
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertIs(value, None)
+
+    def test_coerce(self):
+        tests = []
+        # coerced name, value
+        for args in wrapped_arg_combos('SPAM', '123'):
+            tests.append((args, ('SPAM', '123')))
+        # missing name, value
+        for name in ('', ' ', None, StrProxy(' '), ()):
+            for value in ('', ' ', None, StrProxy(' '), ()):
+                tests.append(
+                        ((name, value), (None, None)))
+        # whitespace
+        tests.extend([
+            ((' SPAM ', ' 123 '), ('SPAM', '123')),
+            ])
+
+        for args, expected in tests:
+            with self.subTest(args):
+                d = Constant(*args)
+
+                self.assertEqual(d[1:], expected)
+                for i, exp in enumerate(expected, start=1):
+                    if exp is not None:
+                        self.assertIs(type(d[i]), str)
+
+    def test_valid(self):
+        tests = [
+            ('SPAM', '123'),
+            # unusual name
+            ('_SPAM_', '123'),
+            ('X_1', '123'),
+            # unusual value
+            ('SPAM', None),
+            ]
+        for args in tests:
+            with self.subTest(args):
+                directive = Constant(*args)
+
+                directive.validate()
+
+    def test_invalid(self):
+        tests = [
+            # invalid name
+            ((None, '123'), TypeError),
+            (('_', '123'), ValueError),
+            (('1', '123'), ValueError),
+            (('_1_', '123'), ValueError),
+            # There is no invalid value (including None).
+            ]
+        for args, exctype in tests:
+            with self.subTest(args):
+                directive = Constant(*args)
+
+                with self.assertRaises(exctype):
+                    directive.validate()
+
+
+class MacroTests(unittest.TestCase):
+
+    def test_type(self):
+        directive = Macro('SPAM', ('x', 'y'), '123')
+
+        self.assertIs(type(directive), Macro)
+        self.assertIsInstance(directive, PreprocessorDirective)
+
+    def test_attrs(self):
+        d = Macro('SPAM', ('x', 'y'), '123')
+        kind, name, args, body = d.kind, d.name, d.args, d.body
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertEqual(args, ('x', 'y'))
+        self.assertEqual(body, '123')
+
+    def test_text(self):
+        tests = [
+            (('SPAM', ('x', 'y'), '123'), 'SPAM(x, y) 123'),
+            (('SPAM', ('x', 'y'),), 'SPAM(x, y)'),
+            ]
+        for args, expected in tests:
+            with self.subTest(args):
+                d = Macro(*args)
+                text = d.text
+
+                self.assertEqual(text, expected)
+
+    def test_iter(self):
+        kind, name, args, body = Macro('SPAM', ('x', 'y'), '123')
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertEqual(args, ('x', 'y'))
+        self.assertEqual(body, '123')
+
+    def test_defaults(self):
+        kind, name, args, body = Macro('SPAM', ('x', 'y'))
+
+        self.assertEqual(kind, 'define')
+        self.assertEqual(name, 'SPAM')
+        self.assertEqual(args, ('x', 'y'))
+        self.assertIs(body, None)
+
+    def test_coerce(self):
+        tests = []
+        # coerce name and body
+        for args in wrapped_arg_combos('SPAM', ('x', 'y'), '123'):
+            tests.append(
+                    (args, ('SPAM', ('x', 'y'), '123')))
+        # coerce args
+        tests.extend([
+            (('SPAM', 'x', '123'),
+             ('SPAM', ('x',), '123')),
+            (('SPAM', 'x,y', '123'),
+             ('SPAM', ('x', 'y'), '123')),
+            ])
+        # coerce arg names
+        for argnames in wrapped_arg_combos('x', 'y'):
+            tests.append(
+                    (('SPAM', argnames, '123'),
+                     ('SPAM', ('x', 'y'), '123')))
+        # missing name, body
+        for name in ('', ' ', None, StrProxy(' '), ()):
+            for argnames in (None, ()):
+                for body in ('', ' ', None, StrProxy(' '), ()):
+                    tests.append(
+                            ((name, argnames, body),
+                             (None, (), None)))
+        # missing args
+        tests.extend([
+            (('SPAM', None, '123'),
+             ('SPAM', (), '123')),
+            (('SPAM', (), '123'),
+             ('SPAM', (), '123')),
+            ])
+        # missing arg names
+        for arg in ('', ' ', None, StrProxy(' '), ()):
+            tests.append(
+                    (('SPAM', (arg,), '123'),
+                     ('SPAM', (None,), '123')))
+        tests.extend([
+            (('SPAM', ('x', '', 'z'), '123'),
+             ('SPAM', ('x', None, 'z'), '123')),
+            ])
+        # whitespace
+        tests.extend([
+            ((' SPAM ', (' x ', ' y '), ' 123 '),
+             ('SPAM', ('x', 'y'), '123')),
+            (('SPAM', 'x, y', '123'),
+             ('SPAM', ('x', 'y'), '123')),
+            ])
+
+        for args, expected in tests:
+            with self.subTest(args):
+                d = Macro(*args)
+
+                self.assertEqual(d[1:], expected)
+                for i, exp in enumerate(expected, start=1):
+                    if i == 2:
+                        self.assertIs(type(d[i]), tuple)
+                    elif exp is not None:
+                        self.assertIs(type(d[i]), str)
+
+    def test_init_bad_args(self):
+        tests = [
+            ('SPAM', StrProxy('x'), '123'),
+            ('SPAM', object(), '123'),
+            ]
+        for args in tests:
+            with self.subTest(args):
+                with self.assertRaises(TypeError):
+                    Macro(*args)
+
+    def test_valid(self):
+        tests = [
+            # unusual name
+            ('SPAM', ('x', 'y'), 'run(x, y)'),
+            ('_SPAM_', ('x', 'y'), 'run(x, y)'),
+            ('X_1', ('x', 'y'), 'run(x, y)'),
+            # unusual args
+            ('SPAM', (), 'run(x, y)'),
+            ('SPAM', ('_x_', 'y_1'), 'run(x, y)'),
+            ('SPAM', 'x', 'run(x, y)'),
+            ('SPAM', 'x, y', 'run(x, y)'),
+            # unusual body
+            ('SPAM', ('x', 'y'), None),
+            ]
+        for args in tests:
+            with self.subTest(args):
+                directive = Macro(*args)
+
+                directive.validate()
+
+    def test_invalid(self):
+        tests = [
+            # invalid name
+            ((None, ('x', 'y'), '123'), TypeError),
+            (('_', ('x', 'y'), '123'), ValueError),
+            (('1', ('x', 'y'), '123'), ValueError),
+            (('_1', ('x', 'y'), '123'), ValueError),
+            # invalid args
+            (('SPAM', (None, 'y'), '123'), ValueError),
+            (('SPAM', ('x', '_'), '123'), ValueError),
+            (('SPAM', ('x', '1'), '123'), ValueError),
+            (('SPAM', ('x', '_1_'), '123'), ValueError),
+            # There is no invalid body (including None).
+            ]
+        for args, exctype in tests:
+            with self.subTest(args):
+                directive = Macro(*args)
+
+                with self.assertRaises(exctype):
+                    directive.validate()
+
+
+class IfDirectiveTests(unittest.TestCase):
+
+    def test_type(self):
+        directive = IfDirective('if', '1')
+
+        self.assertIs(type(directive), IfDirective)
+        self.assertIsInstance(directive, PreprocessorDirective)
+
+    def test_attrs(self):
+        d = IfDirective('if', '1')
+        kind, condition = d.kind, d.condition
+
+        self.assertEqual(kind, 'if')
+        self.assertEqual(condition, '1')
+        #self.assertEqual(condition, (ArithmeticCondition('1'),))
+
+    def test_text(self):
+        tests = [
+            (('if', 'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'),
+             'defined(SPAM) && 1 || (EGGS > 3 && defined(HAM))'),
+            ]
+        for kind in IfDirective.KINDS:
+            tests.append(
+                    ((kind, 'SPAM'), 'SPAM'))
+        for args, expected in tests:
+            with self.subTest(args):
+                d = IfDirective(*args)
+                text = d.text
+
+                self.assertEqual(text, expected)
+
+    def test_iter(self):
+        kind, condition = IfDirective('if', '1')
+
+        self.assertEqual(kind, 'if')
+        self.assertEqual(condition, '1')
+        #self.assertEqual(condition, (ArithmeticCondition('1'),))
+
+    #def test_complex_conditions(self):
+    #    ...
+
+    def test_coerce(self):
+        tests = []
+        for kind in IfDirective.KINDS:
+            if kind == 'ifdef':
+                cond = 'defined(SPAM)'
+            elif kind == 'ifndef':
+                cond = '! defined(SPAM)'
+            else:
+                cond = 'SPAM'
+            for args in wrapped_arg_combos(kind, 'SPAM'):
+                tests.append((args, (kind, cond)))
+            tests.extend([
+                ((' ' + kind + ' ', ' SPAM '), (kind, cond)),
+                ])
+            for raw in ('', ' ', None, StrProxy(' '), ()):
+                tests.append(((kind, raw), (kind, None)))
+        for kind in ('', ' ', None, StrProxy(' '), ()):
+            tests.append(((kind, 'SPAM'), (None, 'SPAM')))
+        for args, expected in tests:
+            with self.subTest(args):
+                d = IfDirective(*args)
+
+                self.assertEqual(tuple(d), expected)
+                for i, exp in enumerate(expected):
+                    if exp is not None:
+                        self.assertIs(type(d[i]), str)
+
+    def test_valid(self):
+        tests = []
+        for kind in IfDirective.KINDS:
+            tests.extend([
+                (kind, 'SPAM'),
+                (kind, '_SPAM_'),
+                (kind, 'X_1'),
+                (kind, '()'),
+                (kind, '--'),
+                (kind, '???'),
+                ])
+        for args in tests:
+            with self.subTest(args):
+                directive = IfDirective(*args)
+
+                directive.validate()
+
+    def test_invalid(self):
+        tests = []
+        # kind
+        tests.extend([
+            ((None, 'SPAM'), TypeError),
+            (('_', 'SPAM'), ValueError),
+            (('-', 'SPAM'), ValueError),
+            (('spam', 'SPAM'), ValueError),
+            ])
+        for kind in PreprocessorDirective.KINDS:
+            if kind in IfDirective.KINDS:
+                continue
+            tests.append(
+                ((kind, 'SPAM'), ValueError))
+        # condition
+        for kind in IfDirective.KINDS:
+            tests.extend([
+                ((kind, None), TypeError),
+                # Any other condition is valid.
+                ])
+        for args, exctype in tests:
+            with self.subTest(args):
+                directive = IfDirective(*args)
+
+                with self.assertRaises(exctype):
+                    directive.validate()
+
+
+class IncludeTests(unittest.TestCase):
+
+    def test_type(self):
+        directive = Include('<stdio>')
+
+        self.assertIs(type(directive), Include)
+        self.assertIsInstance(directive, PreprocessorDirective)
+
+    def test_attrs(self):
+        d = Include('<stdio>')
+        kind, file, text = d.kind, d.file, d.text
+
+        self.assertEqual(kind, 'include')
+        self.assertEqual(file, '<stdio>')
+        self.assertEqual(text, '<stdio>')
+
+    def test_iter(self):
+        kind, file = Include('<stdio>')
+
+        self.assertEqual(kind, 'include')
+        self.assertEqual(file, '<stdio>')
+
+    def test_coerce(self):
+        tests = []
+        for arg, in wrapped_arg_combos('<stdio>'):
+            tests.append((arg, '<stdio>'))
+        tests.extend([
+            (' <stdio> ', '<stdio>'),
+            ])
+        for arg in ('', ' ', None, StrProxy(' '), ()):
+            tests.append((arg, None ))
+        for arg, expected in tests:
+            with self.subTest(arg):
+                _, file = Include(arg)
+
+                self.assertEqual(file, expected)
+                if expected is not None:
+                    self.assertIs(type(file), str)
+
+    def test_valid(self):
+        tests = [
+            '<stdio>',
+            '"spam.h"',
+            '"internal/pycore_pystate.h"',
+            ]
+        for arg in tests:
+            with self.subTest(arg):
+                directive = Include(arg)
+
+                directive.validate()
+
+    def test_invalid(self):
+        tests = [
+            (None, TypeError),
+            # We currently don't check the file.
+            ]
+        for arg, exctype in tests:
+            with self.subTest(arg):
+                directive = Include(arg)
+
+                with self.assertRaises(exctype):
+                    directive.validate()
+
+
+class OtherDirectiveTests(unittest.TestCase):
+
+    def test_type(self):
+        directive = OtherDirective('undef', 'SPAM')
+
+        self.assertIs(type(directive), OtherDirective)
+        self.assertIsInstance(directive, PreprocessorDirective)
+
+    def test_attrs(self):
+        d = OtherDirective('undef', 'SPAM')
+        kind, text = d.kind, d.text
+
+        self.assertEqual(kind, 'undef')
+        self.assertEqual(text, 'SPAM')
+
+    def test_iter(self):
+        kind, text = OtherDirective('undef', 'SPAM')
+
+        self.assertEqual(kind, 'undef')
+        self.assertEqual(text, 'SPAM')
+
+    def test_coerce(self):
+        tests = []
+        for kind in OtherDirective.KINDS:
+            if kind in ('else', 'endif'):
+                continue
+            for args in wrapped_arg_combos(kind, '...'):
+                tests.append((args, (kind, '...')))
+            tests.extend([
+                ((' ' + kind + ' ', ' ... '), (kind, '...')),
+                ])
+            for raw in ('', ' ', None, StrProxy(' '), ()):
+                tests.append(((kind, raw), (kind, None)))
+        for kind in ('else', 'endif'):
+            for args in wrapped_arg_combos(kind, None):
+                tests.append((args, (kind, None)))
+            tests.extend([
+                ((' ' + kind + ' ', None), (kind, None)),
+                ])
+        for kind in ('', ' ', None, StrProxy(' '), ()):
+            tests.append(((kind, '...'), (None, '...')))
+        for args, expected in tests:
+            with self.subTest(args):
+                d = OtherDirective(*args)
+
+                self.assertEqual(tuple(d), expected)
+                for i, exp in enumerate(expected):
+                    if exp is not None:
+                        self.assertIs(type(d[i]), str)
+
+    def test_valid(self):
+        tests = []
+        for kind in OtherDirective.KINDS:
+            if kind in ('else', 'endif'):
+                continue
+            tests.extend([
+                (kind, '...'),
+                (kind, '???'),
+                (kind, 'SPAM'),
+                (kind, '1 + 1'),
+                ])
+        for kind in ('else', 'endif'):
+            tests.append((kind, None))
+        for args in tests:
+            with self.subTest(args):
+                directive = OtherDirective(*args)
+
+                directive.validate()
+
+    def test_invalid(self):
+        tests = []
+        # kind
+        tests.extend([
+            ((None, '...'), TypeError),
+            (('_', '...'), ValueError),
+            (('-', '...'), ValueError),
+            (('spam', '...'), ValueError),
+            ])
+        for kind in PreprocessorDirective.KINDS:
+            if kind in OtherDirective.KINDS:
+                continue
+            tests.append(
+                ((kind, None), ValueError))
+        # text
+        for kind in OtherDirective.KINDS:
+            if kind in ('else', 'endif'):
+                tests.extend([
+                    # Any text is invalid.
+                    ((kind, 'SPAM'), ValueError),
+                    ((kind, '...'), ValueError),
+                    ])
+            else:
+                tests.extend([
+                    ((kind, None), TypeError),
+                    # Any other text is valid.
+                    ])
+        for args, exctype in tests:
+            with self.subTest(args):
+                directive = OtherDirective(*args)
+
+                with self.assertRaises(exctype):
+                    directive.validate()
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_symbols/__init__.py b/Lib/test/test_tools/test_c_analyzer/test_c_symbols/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Lib/test/test_tools/test_c_analyzer/test_c_symbols/test_info.py b/Lib/test/test_tools/test_c_analyzer/test_c_symbols/test_info.py
new file mode 100644 (file)
index 0000000..e029dcf
--- /dev/null
@@ -0,0 +1,192 @@
+import string
+import unittest
+
+from ..util import PseudoStr, StrProxy, Object
+from .. import tool_imports_for_tests
+with tool_imports_for_tests():
+    from c_analyzer_common.info import ID
+    from c_symbols.info import Symbol
+
+
+class SymbolTests(unittest.TestCase):
+
+    VALID_ARGS = (
+            ID('x/y/z/spam.c', 'func', 'eggs'),
+            Symbol.KIND.VARIABLE,
+            False,
+            )
+    VALID_KWARGS = dict(zip(Symbol._fields, VALID_ARGS))
+    VALID_EXPECTED = VALID_ARGS
+
+    def test_init_typical_binary_local(self):
+        id = ID(None, None, 'spam')
+        symbol = Symbol(
+                id=id,
+                kind=Symbol.KIND.VARIABLE,
+                external=False,
+                )
+
+        self.assertEqual(symbol, (
+            id,
+            Symbol.KIND.VARIABLE,
+            False,
+            ))
+
+    def test_init_typical_binary_global(self):
+        id = ID('Python/ceval.c', None, 'spam')
+        symbol = Symbol(
+                id=id,
+                kind=Symbol.KIND.VARIABLE,
+                external=False,
+                )
+
+        self.assertEqual(symbol, (
+            id,
+            Symbol.KIND.VARIABLE,
+            False,
+            ))
+
+    def test_init_coercion(self):
+        tests = [
+            ('str subclass',
+             dict(
+                 id=PseudoStr('eggs'),
+                 kind=PseudoStr('variable'),
+                 external=0,
+                 ),
+             (ID(None, None, 'eggs'),
+              Symbol.KIND.VARIABLE,
+              False,
+              )),
+            ('with filename',
+             dict(
+                 id=('x/y/z/spam.c', 'eggs'),
+                 kind=PseudoStr('variable'),
+                 external=0,
+                 ),
+             (ID('x/y/z/spam.c', None, 'eggs'),
+              Symbol.KIND.VARIABLE,
+              False,
+              )),
+            ('non-str 1',
+             dict(
+                 id=('a', 'b', 'c'),
+                 kind=StrProxy('variable'),
+                 external=0,
+                 ),
+             (ID('a', 'b', 'c'),
+              Symbol.KIND.VARIABLE,
+              False,
+              )),
+            ('non-str 2',
+             dict(
+                 id=('a', 'b', 'c'),
+                 kind=Object(),
+                 external=0,
+                 ),
+             (ID('a', 'b', 'c'),
+              '<object>',
+              False,
+              )),
+            ]
+        for summary, kwargs, expected in tests:
+            with self.subTest(summary):
+                symbol = Symbol(**kwargs)
+
+                for field in Symbol._fields:
+                    value = getattr(symbol, field)
+                    if field == 'external':
+                        self.assertIs(type(value), bool)
+                    elif field == 'id':
+                        self.assertIs(type(value), ID)
+                    else:
+                        self.assertIs(type(value), str)
+                self.assertEqual(tuple(symbol), expected)
+
+    def test_init_all_missing(self):
+        id = ID(None, None, 'spam')
+
+        symbol = Symbol(id)
+
+        self.assertEqual(symbol, (
+            id,
+            Symbol.KIND.VARIABLE,
+            None,
+            ))
+
+    def test_fields(self):
+        id = ID('z', 'x', 'a')
+
+        symbol = Symbol(id, 'b', False)
+
+        self.assertEqual(symbol.id, id)
+        self.assertEqual(symbol.kind, 'b')
+        self.assertIs(symbol.external, False)
+
+    def test___getattr__(self):
+        id = ID('z', 'x', 'a')
+        symbol = Symbol(id, 'b', False)
+
+        filename = symbol.filename
+        funcname = symbol.funcname
+        name = symbol.name
+
+        self.assertEqual(filename, 'z')
+        self.assertEqual(funcname, 'x')
+        self.assertEqual(name, 'a')
+
+    def test_validate_typical(self):
+        id = ID('z', 'x', 'a')
+
+        symbol = Symbol(
+                id=id,
+                kind=Symbol.KIND.VARIABLE,
+                external=False,
+                )
+
+        symbol.validate()  # This does not fail.
+
+    def test_validate_missing_field(self):
+        for field in Symbol._fields:
+            with self.subTest(field):
+                symbol = Symbol(**self.VALID_KWARGS)
+                symbol = symbol._replace(**{field: None})
+
+                with self.assertRaises(TypeError):
+                    symbol.validate()
+
+    def test_validate_bad_field(self):
+        badch = tuple(c for c in string.punctuation + string.digits)
+        notnames = (
+                '1a',
+                'a.b',
+                'a-b',
+                '&a',
+                'a++',
+                ) + badch
+        tests = [
+            ('id', notnames),
+            ('kind', ('bogus',)),
+            ]
+        seen = set()
+        for field, invalid in tests:
+            for value in invalid:
+                if field != 'kind':
+                    seen.add(value)
+                with self.subTest(f'{field}={value!r}'):
+                    symbol = Symbol(**self.VALID_KWARGS)
+                    symbol = symbol._replace(**{field: value})
+
+                    with self.assertRaises(ValueError):
+                        symbol.validate()
+
+        for field, invalid in tests:
+            if field == 'kind':
+                continue
+            valid = seen - set(invalid)
+            for value in valid:
+                with self.subTest(f'{field}={value!r}'):
+                    symbol = Symbol(**self.VALID_KWARGS)
+                    symbol = symbol._replace(**{field: value})
+
+                    symbol.validate()  # This does not fail.
diff --git a/Lib/test/test_tools/test_c_analyzer/util.py b/Lib/test/test_tools/test_c_analyzer/util.py
new file mode 100644 (file)
index 0000000..ba73b0a
--- /dev/null
@@ -0,0 +1,60 @@
+import itertools
+
+
+class PseudoStr(str):
+    pass
+
+
+class StrProxy:
+    def __init__(self, value):
+        self.value = value
+    def __str__(self):
+        return self.value
+    def __bool__(self):
+        return bool(self.value)
+
+
+class Object:
+    def __repr__(self):
+        return '<object>'
+
+
+def wrapped_arg_combos(*args,
+                       wrappers=(PseudoStr, StrProxy),
+                       skip=(lambda w, i, v: not isinstance(v, str)),
+                       ):
+    """Yield every possible combination of wrapped items for the given args.
+
+    Effectively, the wrappers are applied to the args according to the
+    powerset of the args indicies.  So the result includes the args
+    completely unwrapped.
+
+    If "skip" is supplied (default is to skip all non-str values) and
+    it returns True for a given arg index/value then that arg will
+    remain unwrapped,
+
+    Only unique results are returned.  If an arg was skipped for one
+    of the combinations then it could end up matching one of the other
+    combinations.  In that case only one of them will be yielded.
+    """
+    if not args:
+        return
+    indices = list(range(len(args)))
+    # The powerset (from recipe in the itertools docs).
+    combos = itertools.chain.from_iterable(itertools.combinations(indices, r)
+                                           for r in range(len(indices)+1))
+    seen = set()
+    for combo in combos:
+        for wrap in wrappers:
+            indexes = []
+            applied = list(args)
+            for i in combo:
+                arg = args[i]
+                if skip and skip(wrap, i, arg):
+                    continue
+                indexes.append(i)
+                applied[i] = wrap(arg)
+            key = (wrap, tuple(indexes))
+            if key not in seen:
+                yield tuple(applied)
+                seen.add(key)
diff --git a/Tools/c-analyzer/c-globals.py b/Tools/c-analyzer/c-globals.py
new file mode 100644 (file)
index 0000000..9afe059
--- /dev/null
@@ -0,0 +1,9 @@
+# This is a script equivalent of running "python -m test.test_c_globals.cg".
+
+from c_globals.__main__ import parse_args, main
+
+
+# This is effectively copied from cg/__main__.py:
+if __name__ == '__main__':
+    cmd, cmdkwargs = parse_args()
+    main(cmd, cmdkwargs)
diff --git a/Tools/c-analyzer/c_analyzer_common/__init__.py b/Tools/c-analyzer/c_analyzer_common/__init__.py
new file mode 100644 (file)
index 0000000..888b16f
--- /dev/null
@@ -0,0 +1,19 @@
+import os.path
+
+
+PKG_ROOT = os.path.dirname(__file__)
+DATA_DIR = os.path.dirname(PKG_ROOT)
+REPO_ROOT = os.path.dirname(
+        os.path.dirname(DATA_DIR))
+
+SOURCE_DIRS = [os.path.join(REPO_ROOT, name) for name in [
+        'Include',
+        'Python',
+        'Parser',
+        'Objects',
+        'Modules',
+        ]]
+
+
+# Clean up the namespace.
+del os
diff --git a/Tools/c-analyzer/c_analyzer_common/_generate.py b/Tools/c-analyzer/c_analyzer_common/_generate.py
new file mode 100644 (file)
index 0000000..1629aa6
--- /dev/null
@@ -0,0 +1,328 @@
+# The code here consists of hacks for pre-populating the known.tsv file.
+
+from c_parser.preprocessor import _iter_clean_lines
+from c_parser.naive import (
+        iter_variables, parse_variable_declaration, find_variables,
+        )
+from c_parser.info import Variable
+
+from . import SOURCE_DIRS, REPO_ROOT
+from .known import DATA_FILE as KNOWN_FILE, HEADER as KNOWN_HEADER
+from .info import UNKNOWN, ID
+from .util import write_tsv
+from .files import iter_cpython_files
+
+
+POTS = ('char ', 'wchar_t ', 'int ', 'Py_ssize_t ')
+POTS += tuple('const ' + v for v in POTS)
+STRUCTS = ('PyTypeObject', 'PyObject', 'PyMethodDef', 'PyModuleDef', 'grammar')
+
+
+def _parse_global(line, funcname=None):
+    line = line.strip()
+    if line.startswith('static '):
+        if '(' in line and '[' not in line and ' = ' not in line:
+            return None, None
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith(('Py_LOCAL(', 'Py_LOCAL_INLINE(')):
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith('_Py_static_string('):
+        decl = line.strip(';').strip()
+        name = line.split('(')[1].split(',')[0].strip()
+    elif line.startswith('_Py_IDENTIFIER('):
+        decl = line.strip(';').strip()
+        name = 'PyId_' + line.split('(')[1].split(')')[0].strip()
+    elif funcname:
+        return None, None
+
+    # global-only
+    elif line.startswith('PyAPI_DATA('):  # only in .h files
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith('extern '):  # only in .h files
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith('PyDoc_VAR('):
+        decl = line.strip(';').strip()
+        name = line.split('(')[1].split(')')[0].strip()
+    elif line.startswith(POTS):  # implied static
+        if '(' in line and '[' not in line and ' = ' not in line:
+            return None, None
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith(STRUCTS) and line.endswith(' = {'):  # implied static
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith(STRUCTS) and line.endswith(' = NULL;'):  # implied static
+        name, decl = parse_variable_declaration(line)
+    elif line.startswith('struct '):
+        if not line.endswith(' = {'):
+            return None, None
+        if not line.partition(' ')[2].startswith(STRUCTS):
+            return None, None
+        # implied static
+        name, decl = parse_variable_declaration(line)
+
+    # file-specific
+    elif line.startswith(('SLOT1BINFULL(', 'SLOT1BIN(')):
+        # Objects/typeobject.c
+        funcname = line.split('(')[1].split(',')[0]
+        return [
+                ('op_id', funcname, '_Py_static_string(op_id, OPSTR)'),
+                ('rop_id', funcname, '_Py_static_string(op_id, OPSTR)'),
+                ]
+    elif line.startswith('WRAP_METHOD('):
+        # Objects/weakrefobject.c
+        funcname, name = (v.strip() for v in line.split('(')[1].split(')')[0].split(','))
+        return [
+                ('PyId_' + name, funcname, f'_Py_IDENTIFIER({name})'),
+                ]
+
+    else:
+        return None, None
+    return name, decl
+
+
+def _pop_cached(varcache, filename, funcname, name, *,
+                _iter_variables=iter_variables,
+                ):
+    # Look for the file.
+    try:
+        cached = varcache[filename]
+    except KeyError:
+        cached = varcache[filename] = {}
+        for variable in _iter_variables(filename,
+                                        parse_variable=_parse_global,
+                                        ):
+            variable._isglobal = True
+            cached[variable.id] = variable
+        for var in cached:
+            print(' ', var)
+
+    # Look for the variable.
+    if funcname == UNKNOWN:
+        for varid in cached:
+            if varid.name == name:
+                break
+        else:
+            return None
+        return cached.pop(varid)
+    else:
+        return cached.pop((filename, funcname, name), None)
+
+
+def find_matching_variable(varid, varcache, allfilenames, *,
+                           _pop_cached=_pop_cached,
+                           ):
+    if varid.filename and varid.filename != UNKNOWN:
+        filenames = [varid.filename]
+    else:
+        filenames = allfilenames
+    for filename in filenames:
+        variable = _pop_cached(varcache, filename, varid.funcname, varid.name)
+        if variable is not None:
+            return variable
+    else:
+        if varid.filename and varid.filename != UNKNOWN and varid.funcname is None:
+            for filename in allfilenames:
+                if not filename.endswith('.h'):
+                    continue
+                variable = _pop_cached(varcache, filename, None, varid.name)
+                if variable is not None:
+                    return variable
+        return None
+
+
+MULTILINE = {
+    # Python/Python-ast.c
+    'Load_singleton': 'PyObject *',
+    'Store_singleton': 'PyObject *',
+    'Del_singleton': 'PyObject *',
+    'AugLoad_singleton': 'PyObject *',
+    'AugStore_singleton': 'PyObject *',
+    'Param_singleton': 'PyObject *',
+    'And_singleton': 'PyObject *',
+    'Or_singleton': 'PyObject *',
+    'Add_singleton': 'static PyObject *',
+    'Sub_singleton': 'static PyObject *',
+    'Mult_singleton': 'static PyObject *',
+    'MatMult_singleton': 'static PyObject *',
+    'Div_singleton': 'static PyObject *',
+    'Mod_singleton': 'static PyObject *',
+    'Pow_singleton': 'static PyObject *',
+    'LShift_singleton': 'static PyObject *',
+    'RShift_singleton': 'static PyObject *',
+    'BitOr_singleton': 'static PyObject *',
+    'BitXor_singleton': 'static PyObject *',
+    'BitAnd_singleton': 'static PyObject *',
+    'FloorDiv_singleton': 'static PyObject *',
+    'Invert_singleton': 'static PyObject *',
+    'Not_singleton': 'static PyObject *',
+    'UAdd_singleton': 'static PyObject *',
+    'USub_singleton': 'static PyObject *',
+    'Eq_singleton': 'static PyObject *',
+    'NotEq_singleton': 'static PyObject *',
+    'Lt_singleton': 'static PyObject *',
+    'LtE_singleton': 'static PyObject *',
+    'Gt_singleton': 'static PyObject *',
+    'GtE_singleton': 'static PyObject *',
+    'Is_singleton': 'static PyObject *',
+    'IsNot_singleton': 'static PyObject *',
+    'In_singleton': 'static PyObject *',
+    'NotIn_singleton': 'static PyObject *',
+    # Python/symtable.c
+    'top': 'static identifier ',
+    'lambda': 'static identifier ',
+    'genexpr': 'static identifier ',
+    'listcomp': 'static identifier ',
+    'setcomp': 'static identifier ',
+    'dictcomp': 'static identifier ',
+    '__class__': 'static identifier ',
+    # Python/compile.c
+    '__doc__': 'static PyObject *',
+    '__annotations__': 'static PyObject *',
+    # Objects/floatobject.c
+    'double_format': 'static float_format_type ',
+    'float_format': 'static float_format_type ',
+    'detected_double_format': 'static float_format_type ',
+    'detected_float_format': 'static float_format_type ',
+    # Parser/listnode.c
+    'level': 'static int ',
+    'atbol': 'static int ',
+    # Python/dtoa.c
+    'private_mem': 'static double private_mem[PRIVATE_mem]',
+    'pmem_next': 'static double *',
+    # Modules/_weakref.c
+    'weakref_functions': 'static PyMethodDef ',
+}
+INLINE = {
+    # Modules/_tracemalloc.c
+    'allocators': 'static struct { PyMemAllocatorEx mem; PyMemAllocatorEx raw; PyMemAllocatorEx obj; } ',
+    # Modules/faulthandler.c
+    'fatal_error': 'static struct { int enabled; PyObject *file; int fd; int all_threads; PyInterpreterState *interp; void *exc_handler; } ',
+    'thread': 'static struct { PyObject *file; int fd; PY_TIMEOUT_T timeout_us; int repeat; PyInterpreterState *interp; int exit; char *header; size_t header_len; PyThread_type_lock cancel_event; PyThread_type_lock running; } ',
+    # Modules/signalmodule.c
+    'Handlers': 'static volatile struct { _Py_atomic_int tripped; PyObject *func; } Handlers[NSIG]',
+    'wakeup': 'static volatile struct { SOCKET_T fd; int warn_on_full_buffer; int use_send; } ',
+    # Python/dynload_shlib.c
+    'handles': 'static struct { dev_t dev; ino_t ino; void *handle; } handles[128]',
+    # Objects/obmalloc.c
+    '_PyMem_Debug': 'static struct { debug_alloc_api_t raw; debug_alloc_api_t mem; debug_alloc_api_t obj; } ',
+    # Python/bootstrap_hash.c
+    'urandom_cache': 'static struct { int fd; dev_t st_dev; ino_t st_ino; } ',
+    }
+FUNC = {
+    # Objects/object.c
+    '_Py_abstract_hack': 'Py_ssize_t (*_Py_abstract_hack)(PyObject *)',
+    # Parser/myreadline.c
+    'PyOS_InputHook': 'int (*PyOS_InputHook)(void)',
+    # Python/pylifecycle.c
+    '_PyOS_mystrnicmp_hack': 'int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t)',
+    # Parser/myreadline.c
+    'PyOS_ReadlineFunctionPointer': 'char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *)',
+    }
+IMPLIED = {
+    # Objects/boolobject.c
+    '_Py_FalseStruct': 'static struct _longobject ',
+    '_Py_TrueStruct': 'static struct _longobject ',
+    # Modules/config.c
+    '_PyImport_Inittab': 'struct _inittab _PyImport_Inittab[]',
+    }
+GLOBALS = {}
+GLOBALS.update(MULTILINE)
+GLOBALS.update(INLINE)
+GLOBALS.update(FUNC)
+GLOBALS.update(IMPLIED)
+
+LOCALS = {
+    'buildinfo': ('Modules/getbuildinfo.c',
+                  'Py_GetBuildInfo',
+                  'static char buildinfo[50 + sizeof(GITVERSION) + ((sizeof(GITTAG) > sizeof(GITBRANCH)) ?  sizeof(GITTAG) : sizeof(GITBRANCH))]'),
+    'methods': ('Python/codecs.c',
+                '_PyCodecRegistry_Init',
+                'static struct { char *name; PyMethodDef def; } methods[]'),
+    }
+
+
+def _known(symbol):
+    if symbol.funcname:
+        if symbol.funcname != UNKNOWN or symbol.filename != UNKNOWN:
+            raise KeyError(symbol.name)
+        filename, funcname, decl = LOCALS[symbol.name]
+        varid = ID(filename, funcname, symbol.name)
+    elif not symbol.filename or symbol.filename == UNKNOWN:
+        raise KeyError(symbol.name)
+    else:
+        varid = symbol.id
+        try:
+            decl = GLOBALS[symbol.name]
+        except KeyError:
+
+            if symbol.name.endswith('_methods'):
+                decl = 'static PyMethodDef '
+            elif symbol.filename == 'Objects/exceptions.c' and symbol.name.startswith(('PyExc_', '_PyExc_')):
+                decl = 'static PyTypeObject '
+            else:
+                raise
+    if symbol.name not in decl:
+        decl = decl + symbol.name
+    return Variable(varid, decl)
+
+
+def known_row(varid, decl):
+    return (
+            varid.filename,
+            varid.funcname or '-',
+            varid.name,
+            'variable',
+            decl,
+            )
+
+
+def known_rows(symbols, *,
+               cached=True,
+               _get_filenames=iter_cpython_files,
+               _find_match=find_matching_variable,
+               _find_symbols=find_variables,
+               _as_known=known_row,
+               ):
+    filenames = list(_get_filenames())
+    cache = {}
+    if cached:
+        for symbol in symbols:
+            try:
+                found = _known(symbol)
+            except KeyError:
+                found = _find_match(symbol, cache, filenames)
+                if found is None:
+                    found = Variable(symbol.id, UNKNOWN)
+            yield _as_known(found.id, found.vartype)
+    else:
+        raise NotImplementedError  # XXX incorporate KNOWN
+        for variable in _find_symbols(symbols, filenames,
+                                      srccache=cache,
+                                      parse_variable=_parse_global,
+                                      ):
+            #variable = variable._replace(
+            #    filename=os.path.relpath(variable.filename, REPO_ROOT))
+            if variable.funcname == UNKNOWN:
+                print(variable)
+            if variable.vartype== UNKNOWN:
+                print(variable)
+            yield _as_known(variable.id, variable.vartype)
+
+
+def generate(symbols, filename=None, *,
+             _generate_rows=known_rows,
+             _write_tsv=write_tsv,
+             ):
+    if not filename:
+        filename = KNOWN_FILE + '.new'
+
+    rows = _generate_rows(symbols)
+    _write_tsv(filename, KNOWN_HEADER, rows)
+
+
+if __name__ == '__main__':
+    from c_symbols import binary
+    symbols = binary.iter_symbols(
+            binary.PYTHON,
+            find_local_symbol=None,
+            )
+    generate(symbols)
diff --git a/Tools/c-analyzer/c_analyzer_common/files.py b/Tools/c-analyzer/c_analyzer_common/files.py
new file mode 100644 (file)
index 0000000..b3cd16c
--- /dev/null
@@ -0,0 +1,138 @@
+import glob
+import os
+import os.path
+
+from . import SOURCE_DIRS, REPO_ROOT
+
+
+C_SOURCE_SUFFIXES = ('.c', '.h')
+
+
+def _walk_tree(root, *,
+               _walk=os.walk,
+               ):
+    # A wrapper around os.walk that resolves the filenames.
+    for parent, _, names in _walk(root):
+        for name in names:
+            yield os.path.join(parent, name)
+
+
+def walk_tree(root, *,
+              suffix=None,
+              walk=_walk_tree,
+              ):
+    """Yield each file in the tree under the given directory name.
+
+    If "suffix" is provided then only files with that suffix will
+    be included.
+    """
+    if suffix and not isinstance(suffix, str):
+        raise ValueError('suffix must be a string')
+
+    for filename in walk(root):
+        if suffix and not filename.endswith(suffix):
+            continue
+        yield filename
+
+
+def glob_tree(root, *,
+              suffix=None,
+              _glob=glob.iglob,
+              ):
+    """Yield each file in the tree under the given directory name.
+
+    If "suffix" is provided then only files with that suffix will
+    be included.
+    """
+    suffix = suffix or ''
+    if not isinstance(suffix, str):
+        raise ValueError('suffix must be a string')
+
+    for filename in _glob(f'{root}/*{suffix}'):
+        yield filename
+    for filename in _glob(f'{root}/**/*{suffix}'):
+        yield filename
+
+
+def iter_files(root, suffix=None, relparent=None, *,
+               get_files=os.walk,
+               _glob=glob_tree,
+               _walk=walk_tree,
+               ):
+    """Yield each file in the tree under the given directory name.
+
+    If "root" is a non-string iterable then do the same for each of
+    those trees.
+
+    If "suffix" is provided then only files with that suffix will
+    be included.
+
+    if "relparent" is provided then it is used to resolve each
+    filename as a relative path.
+    """
+    if not isinstance(root, str):
+        roots = root
+        for root in roots:
+            yield from iter_files(root, suffix, relparent,
+                                  get_files=get_files,
+                                  _glob=_glob, _walk=_walk)
+        return
+
+    # Use the right "walk" function.
+    if get_files in (glob.glob, glob.iglob, glob_tree):
+        get_files = _glob
+    else:
+        _files = _walk_tree if get_files in (os.walk, walk_tree) else get_files
+        get_files = (lambda *a, **k: _walk(*a, walk=_files, **k))
+
+    # Handle a single suffix.
+    if suffix and not isinstance(suffix, str):
+        filenames = get_files(root)
+        suffix = tuple(suffix)
+    else:
+        filenames = get_files(root, suffix=suffix)
+        suffix = None
+
+    for filename in filenames:
+        if suffix and not isinstance(suffix, str):  # multiple suffixes
+            if not filename.endswith(suffix):
+                continue
+        if relparent:
+            filename = os.path.relpath(filename, relparent)
+        yield filename
+
+
+def iter_files_by_suffix(root, suffixes, relparent=None, *,
+                         walk=walk_tree,
+                         _iter_files=iter_files,
+                         ):
+    """Yield each file in the tree that has the given suffixes.
+
+    Unlike iter_files(), the results are in the original suffix order.
+    """
+    if isinstance(suffixes, str):
+        suffixes = [suffixes]
+    # XXX Ignore repeated suffixes?
+    for suffix in suffixes:
+        yield from _iter_files(root, suffix, relparent)
+
+
+def iter_cpython_files(*,
+                       walk=walk_tree,
+                       _files=iter_files_by_suffix,
+                       ):
+    """Yield each file in the tree for each of the given directory names."""
+    excludedtrees = [
+        os.path.join('Include', 'cpython', ''),
+        ]
+    def is_excluded(filename):
+        for root in excludedtrees:
+            if filename.startswith(root):
+                return True
+        return False
+    for filename in _files(SOURCE_DIRS, C_SOURCE_SUFFIXES, REPO_ROOT,
+                           walk=walk,
+                           ):
+        if is_excluded(filename):
+            continue
+        yield filename
diff --git a/Tools/c-analyzer/c_analyzer_common/info.py b/Tools/c-analyzer/c_analyzer_common/info.py
new file mode 100644 (file)
index 0000000..e217380
--- /dev/null
@@ -0,0 +1,69 @@
+from collections import namedtuple
+import re
+
+from .util import classonly, _NTBase
+
+
+UNKNOWN = '???'
+
+NAME_RE = re.compile(r'^([a-zA-Z]|_\w*[a-zA-Z]\w*|[a-zA-Z]\w*)$')
+
+
+class ID(_NTBase, namedtuple('ID', 'filename funcname name')):
+    """A unique ID for a single symbol or declaration."""
+
+    __slots__ = ()
+    # XXX Add optional conditions (tuple of strings) field.
+    #conditions = Slot()
+
+    @classonly
+    def from_raw(cls, raw):
+        if not raw:
+            return None
+        if isinstance(raw, str):
+            return cls(None, None, raw)
+        try:
+            name, = raw
+            filename = None
+        except ValueError:
+            try:
+                filename, name = raw
+            except ValueError:
+                return super().from_raw(raw)
+        return cls(filename, None, name)
+
+    def __new__(cls, filename, funcname, name):
+        self = super().__new__(
+                cls,
+                filename=str(filename) if filename else None,
+                funcname=str(funcname) if funcname else None,
+                name=str(name) if name else None,
+                )
+        #cls.conditions.set(self, tuple(str(s) if s else None
+        #                               for s in conditions or ()))
+        return self
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        if not self.name:
+            raise TypeError('missing name')
+        else:
+            if not NAME_RE.match(self.name):
+                raise ValueError(
+                        f'name must be an identifier, got {self.name!r}')
+
+        # Symbols from a binary might not have filename/funcname info.
+
+        if self.funcname:
+            if not self.filename:
+                raise TypeError('missing filename')
+            if not NAME_RE.match(self.funcname) and self.funcname != UNKNOWN:
+                raise ValueError(
+                        f'name must be an identifier, got {self.funcname!r}')
+
+        # XXX Require the filename (at least UNKONWN)?
+        # XXX Check the filename?
+
+    @property
+    def islocal(self):
+        return self.funcname is not None
diff --git a/Tools/c-analyzer/c_analyzer_common/known.py b/Tools/c-analyzer/c_analyzer_common/known.py
new file mode 100644 (file)
index 0000000..a0c6dfa
--- /dev/null
@@ -0,0 +1,67 @@
+import csv
+import os.path
+
+from c_parser.info import Variable
+
+from . import DATA_DIR
+from .info import ID, UNKNOWN
+from .util import read_tsv
+
+
+DATA_FILE = os.path.join(DATA_DIR, 'known.tsv')
+
+COLUMNS = ('filename', 'funcname', 'name', 'kind', 'declaration')
+HEADER = '\t'.join(COLUMNS)
+
+
+# XXX need tests:
+# * from_file()
+
+def from_file(infile, *,
+              _read_tsv=read_tsv,
+              ):
+    """Return the info for known declarations in the given file."""
+    known = {
+        'variables': {},
+        #'types': {},
+        #'constants': {},
+        #'macros': {},
+        }
+    for row in _read_tsv(infile, HEADER):
+        filename, funcname, name, kind, declaration = row
+        if not funcname or funcname == '-':
+            funcname = None
+        id = ID(filename, funcname, name)
+        if kind == 'variable':
+            values = known['variables']
+            value = Variable(id, declaration)
+            value._isglobal = _is_global(declaration) or id.funcname is None
+        else:
+            raise ValueError(f'unsupported kind in row {row}')
+        if value.name == 'id' and declaration == UNKNOWN:
+            # None of these are variables.
+            declaration = 'int id';
+        else:
+            value.validate()
+        values[id] = value
+    return known
+
+
+def _is_global(vartype):
+    # statics
+    if vartype.startswith('static '):
+        return True
+    if vartype.startswith(('Py_LOCAL(', 'Py_LOCAL_INLINE(')):
+        return True
+    if vartype.startswith(('_Py_IDENTIFIER(', '_Py_static_string(')):
+        return True
+    if vartype.startswith('PyDoc_VAR('):
+        return True
+    if vartype.startswith(('SLOT1BINFULL(', 'SLOT1BIN(')):
+        return True
+    if vartype.startswith('WRAP_METHOD('):
+        return True
+    # public extern
+    if vartype.startswith('PyAPI_DATA('):
+        return True
+    return False
diff --git a/Tools/c-analyzer/c_analyzer_common/util.py b/Tools/c-analyzer/c_analyzer_common/util.py
new file mode 100644 (file)
index 0000000..511c54f
--- /dev/null
@@ -0,0 +1,214 @@
+import csv
+import subprocess
+
+
+_NOT_SET = object()
+
+
+def run_cmd(argv, **kwargs):
+    proc = subprocess.run(
+            argv,
+            #capture_output=True,
+            #stderr=subprocess.STDOUT,
+            stdout=subprocess.PIPE,
+            text=True,
+            check=True,
+            **kwargs
+            )
+    return proc.stdout
+
+
+def read_tsv(infile, header, *,
+             _open=open,
+             _get_reader=csv.reader,
+             ):
+    """Yield each row of the given TSV (tab-separated) file."""
+    if isinstance(infile, str):
+        with _open(infile, newline='') as infile:
+            yield from read_tsv(infile, header,
+                                _open=_open,
+                                _get_reader=_get_reader,
+                                )
+            return
+    lines = iter(infile)
+
+    # Validate the header.
+    try:
+        actualheader = next(lines).strip()
+    except StopIteration:
+        actualheader = ''
+    if actualheader != header:
+        raise ValueError(f'bad header {actualheader!r}')
+
+    for row in _get_reader(lines, delimiter='\t'):
+        yield tuple(v.strip() for v in row)
+
+
+def write_tsv(outfile, header, rows, *,
+             _open=open,
+             _get_writer=csv.writer,
+             ):
+    """Write each of the rows to the given TSV (tab-separated) file."""
+    if isinstance(outfile, str):
+        with _open(outfile, 'w', newline='') as outfile:
+            return write_tsv(outfile, header, rows,
+                            _open=_open,
+                            _get_writer=_get_writer,
+                            )
+
+    if isinstance(header, str):
+        header = header.split('\t')
+    writer = _get_writer(outfile, delimiter='\t')
+    writer.writerow(header)
+    for row in rows:
+        writer.writerow('' if v is None else str(v)
+                        for v in row)
+
+
+class Slot:
+    """A descriptor that provides a slot.
+
+    This is useful for types that can't have slots via __slots__,
+    e.g. tuple subclasses.
+    """
+
+    __slots__ = ('initial', 'default', 'readonly', 'instances', 'name')
+
+    def __init__(self, initial=_NOT_SET, *,
+                 default=_NOT_SET,
+                 readonly=False,
+                 ):
+        self.initial = initial
+        self.default = default
+        self.readonly = readonly
+
+        self.instances = {}
+        self.name = None
+
+    def __set_name__(self, cls, name):
+        if self.name is not None:
+            raise TypeError('already used')
+        self.name = name
+
+    def __get__(self, obj, cls):
+        if obj is None:  # called on the class
+            return self
+        try:
+            value = self.instances[id(obj)]
+        except KeyError:
+            if self.initial is _NOT_SET:
+                value = self.default
+            else:
+                value = self.initial
+            self.instances[id(obj)] = value
+        if value is _NOT_SET:
+            raise AttributeError(self.name)
+        # XXX Optionally make a copy?
+        return value
+
+    def __set__(self, obj, value):
+        if self.readonly:
+            raise AttributeError(f'{self.name} is readonly')
+        # XXX Optionally coerce?
+        self.instances[id(obj)] = value
+
+    def __delete__(self, obj):
+        if self.readonly:
+            raise AttributeError(f'{self.name} is readonly')
+        self.instances[id(obj)] = self.default
+
+    def set(self, obj, value):
+        """Update the cached value for an object.
+
+        This works even if the descriptor is read-only.  This is
+        particularly useful when initializing the object (e.g. in
+        its __new__ or __init__).
+        """
+        self.instances[id(obj)] = value
+
+
+class classonly:
+    """A non-data descriptor that makes a value only visible on the class.
+
+    This is like the "classmethod" builtin, but does not show up on
+    instances of the class.  It may be used as a decorator.
+    """
+
+    def __init__(self, value):
+        self.value = value
+        self.getter = classmethod(value).__get__
+        self.name = None
+
+    def __set_name__(self, cls, name):
+        if self.name is not None:
+            raise TypeError('already used')
+        self.name = name
+
+    def __get__(self, obj, cls):
+        if obj is not None:
+            raise AttributeError(self.name)
+        # called on the class
+        return self.getter(None, cls)
+
+
+class _NTBase:
+
+    __slots__ = ()
+
+    @classonly
+    def from_raw(cls, raw):
+        if not raw:
+            return None
+        elif isinstance(raw, cls):
+            return raw
+        elif isinstance(raw, str):
+            return cls.from_string(raw)
+        else:
+            if hasattr(raw, 'items'):
+                return cls(**raw)
+            try:
+                args = tuple(raw)
+            except TypeError:
+                pass
+            else:
+                return cls(*args)
+        raise NotImplementedError
+
+    @classonly
+    def from_string(cls, value):
+        """Return a new instance based on the given string."""
+        raise NotImplementedError
+
+    @classmethod
+    def _make(cls, iterable):  # The default _make() is not subclass-friendly.
+        return cls.__new__(cls, *iterable)
+
+    # XXX Always validate?
+    #def __init__(self, *args, **kwargs):
+    #    self.validate()
+
+    # XXX The default __repr__() is not subclass-friendly (where the name changes).
+    #def __repr__(self):
+    #    _, _, sig = super().__repr__().partition('(')
+    #    return f'{self.__class__.__name__}({sig}'
+
+    # To make sorting work with None:
+    def __lt__(self, other):
+        try:
+            return super().__lt__(other)
+        except TypeError:
+            if None in self:
+                return True
+            elif None in other:
+                return False
+            else:
+                raise
+
+    def validate(self):
+        return
+
+    # XXX Always validate?
+    #def _replace(self, **kwargs):
+    #    obj = super()._replace(**kwargs)
+    #    obj.validate()
+    #    return obj
diff --git a/Tools/c-analyzer/c_globals/README b/Tools/c-analyzer/c_globals/README
new file mode 100644 (file)
index 0000000..772b8be
--- /dev/null
@@ -0,0 +1,72 @@
+#######################################
+# C Globals and CPython Runtime State.
+
+CPython's C code makes extensive use of global variables (whether static
+globals or static locals).  Each such variable falls into one of several
+categories:
+
+* strictly const data
+* used exclusively in main or in the REPL
+* process-global state (e.g. managing process-level resources
+  like signals and file descriptors)
+* Python "global" runtime state
+* per-interpreter runtime state
+
+The last one can be a problem as soon as anyone creates a second
+interpreter (AKA "subinterpreter") in a process.  It is definitely a
+problem under subinterpreters if they are no longer sharing the GIL,
+since the GIL protects us from a lot of race conditions.  Keep in mind
+that ultimately *all* objects (PyObject) should be treated as
+per-interpreter state.  This includes "static types", freelists,
+_PyIdentifier, and singletons.  Take that in for a second.  It has
+significant implications on where we use static variables!
+
+Be aware that module-global state (stored in C statics) is a kind of
+per-interpreter state.  There have been efforts across many years, and
+still going, to provide extension module authors mechanisms to store
+that state safely (see PEPs 3121, 489, etc.).
+
+(Note that there has been discussion around support for running multiple
+Python runtimes in the same process.  That would ends up with the same
+problems, relative to static variables, that subinterpreters have.)
+
+Historically we have been bad at keeping per-interpreter state out of
+static variables, mostly because until recently subinterpreters were
+not widely used nor even factored in to solutions.  However, the
+feature is growing in popularity and use in the community.
+
+Mandate: "Eliminate use of static variables for per-interpreter state."
+
+The "c-statics.py" script in this directory, along with its accompanying
+data files, are part of the effort to resolve existing problems with
+our use of static variables and to prevent future problems.
+
+#-------------------------
+## statics for actually-global state (and runtime state consolidation)
+
+In general, holding any kind of state in static variables
+increases maintenance burden and increases the complexity of code (e.g.
+we use TSS to identify the active thread state).  So it is a good idea
+to avoid using statics for state even if for the "global" runtime or
+for process-global state.
+
+Relative to maintenance burden, one problem is where the runtime
+state is spread throughout the codebase in dozens of individual
+globals.  Unlike the other globals, the runtime state represents a set
+of values that are constantly shifting in a complex way.  When they are
+spread out it's harder to get a clear picture of what the runtime
+involves.  Furthermore, when they are spread out it complicates efforts
+that change the runtime.
+
+Consequently, the globals for Python's runtime state have been
+consolidated under a single top-level _PyRuntime global. No new globals
+should be added for runtime state.  Instead, they should be added to
+_PyRuntimeState or one of its sub-structs.  The tools in this directory
+are run as part of the test suite to ensure that no new globals have
+been added.  The script can be run manually as well:
+
+  ./python Lib/test/test_c_statics/c-statics.py check
+
+If it reports any globals then they should be resolved.  If the globals
+are runtime state then they should be folded into _PyRuntimeState.
+Otherwise they should be marked as ignored.
diff --git a/Tools/c-analyzer/c_globals/__init__.py b/Tools/c-analyzer/c_globals/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tools/c-analyzer/c_globals/__main__.py b/Tools/c-analyzer/c_globals/__main__.py
new file mode 100644 (file)
index 0000000..9570fb6
--- /dev/null
@@ -0,0 +1,209 @@
+import argparse
+import os.path
+import re
+import sys
+
+from c_analyzer_common import SOURCE_DIRS, REPO_ROOT
+from c_analyzer_common.info import UNKNOWN
+from c_analyzer_common.known import (
+    from_file as known_from_file,
+    DATA_FILE as KNOWN_FILE,
+    )
+from . import find, show
+from .supported import is_supported, ignored_from_file, IGNORED_FILE, _is_object
+
+
+def _match_unused_global(variable, knownvars, used):
+    found = []
+    for varid in knownvars:
+        if varid in used:
+            continue
+        if varid.funcname is not None:
+            continue
+        if varid.name != variable.name:
+            continue
+        if variable.filename and variable.filename != UNKNOWN:
+            if variable.filename == varid.filename:
+                found.append(varid)
+        else:
+            found.append(varid)
+    return found
+
+
+def _check_results(unknown, knownvars, used):
+    badknown = set()
+    for variable in sorted(unknown):
+        msg = None
+        if variable.funcname != UNKNOWN:
+            msg = f'could not find global symbol {variable.id}'
+        elif m := _match_unused_global(variable, knownvars, used):
+            assert isinstance(m, list)
+            badknown.update(m)
+        elif variable.name in ('completed', 'id'):  # XXX Figure out where these variables are.
+            unknown.remove(variable)
+        else:
+            msg = f'could not find local symbol {variable.id}'
+        if msg:
+            #raise Exception(msg)
+            print(msg)
+    if badknown:
+        print('---')
+        print(f'{len(badknown)} globals in known.tsv, but may actually be local:')
+        for varid in sorted(badknown):
+            print(f'{varid.filename:30} {varid.name}')
+    unused = sorted(varid
+                    for varid in set(knownvars) - used
+                    if varid.name != 'id')  # XXX Figure out where these variables are.
+    if unused:
+        print('---')
+        print(f'did not use {len(unused)} known vars:')
+        for varid in unused:
+            print(f'{varid.filename:30} {varid.funcname or "-":20} {varid.name}')
+        raise Exception('not all known symbols used')
+    if unknown:
+        print('---')
+        raise Exception('could not find all symbols')
+
+
+def _find_globals(dirnames, known, ignored):
+    if dirnames == SOURCE_DIRS:
+        dirnames = [os.path.relpath(d, REPO_ROOT) for d in dirnames]
+
+    ignored = ignored_from_file(ignored)
+    known = known_from_file(known)
+
+    used = set()
+    unknown = set()
+    knownvars = (known or {}).get('variables')
+    for variable in find.globals_from_binary(knownvars=knownvars,
+                                             dirnames=dirnames):
+    #for variable in find.globals(dirnames, known, kind='platform'):
+        if variable.vartype == UNKNOWN:
+            unknown.add(variable)
+            continue
+        yield variable, is_supported(variable, ignored, known)
+        used.add(variable.id)
+
+    #_check_results(unknown, knownvars, used)
+
+
+def cmd_check(cmd, dirs=SOURCE_DIRS, *,
+              ignored=IGNORED_FILE,
+              known=KNOWN_FILE,
+              _find=_find_globals,
+              _show=show.basic,
+              _print=print,
+              ):
+    """
+    Fail if there are unsupported globals variables.
+
+    In the failure case, the list of unsupported variables
+    will be printed out.
+    """
+    unsupported = [v for v, s in _find(dirs, known, ignored) if not s]
+    if not unsupported:
+        #_print('okay')
+        return
+
+    _print('ERROR: found unsupported global variables')
+    _print()
+    _show(sorted(unsupported))
+    _print(f' ({len(unsupported)} total)')
+    sys.exit(1)
+
+
+def cmd_show(cmd, dirs=SOURCE_DIRS, *,
+             ignored=IGNORED_FILE,
+             known=KNOWN_FILE,
+             skip_objects=False,
+              _find=_find_globals,
+             _show=show.basic,
+             _print=print,
+             ):
+    """
+    Print out the list of found global variables.
+
+    The variables will be distinguished as "supported" or "unsupported".
+    """
+    allsupported = []
+    allunsupported = []
+    for found, supported in _find(dirs, known, ignored):
+        if skip_objects:  # XXX Support proper filters instead.
+            if _is_object(found.vartype):
+                continue
+        (allsupported if supported else allunsupported
+         ).append(found)
+
+    _print('supported:')
+    _print('----------')
+    _show(sorted(allsupported))
+    _print(f' ({len(allsupported)} total)')
+    _print()
+    _print('unsupported:')
+    _print('------------')
+    _show(sorted(allunsupported))
+    _print(f' ({len(allunsupported)} total)')
+
+
+#############################
+# the script
+
+COMMANDS = {
+        'check': cmd_check,
+        'show': cmd_show,
+        }
+
+PROG = sys.argv[0]
+PROG = 'c-globals.py'
+
+
+def parse_args(prog=PROG, argv=sys.argv[1:], *, _fail=None):
+    common = argparse.ArgumentParser(add_help=False)
+    common.add_argument('--ignored', metavar='FILE',
+                        default=IGNORED_FILE,
+                        help='path to file that lists ignored vars')
+    common.add_argument('--known', metavar='FILE',
+                        default=KNOWN_FILE,
+                        help='path to file that lists known types')
+    common.add_argument('dirs', metavar='DIR', nargs='*',
+                        default=SOURCE_DIRS,
+                        help='a directory to check')
+
+    parser = argparse.ArgumentParser(
+            prog=prog,
+            )
+    subs = parser.add_subparsers(dest='cmd')
+
+    check = subs.add_parser('check', parents=[common])
+
+    show = subs.add_parser('show', parents=[common])
+    show.add_argument('--skip-objects', action='store_true')
+
+    if _fail is None:
+        def _fail(msg):
+            parser.error(msg)
+
+    # Now parse the args.
+    args = parser.parse_args(argv)
+    ns = vars(args)
+
+    cmd = ns.pop('cmd')
+    if not cmd:
+        _fail('missing command')
+
+    return cmd, ns
+
+
+def main(cmd, cmdkwargs=None, *, _COMMANDS=COMMANDS):
+    try:
+        cmdfunc = _COMMANDS[cmd]
+    except KeyError:
+        raise ValueError(
+            f'unsupported cmd {cmd!r}' if cmd else 'missing cmd')
+
+    cmdfunc(cmd, **cmdkwargs or {})
+
+
+if __name__ == '__main__':
+    cmd, cmdkwargs = parse_args()
+    main(cmd, cmdkwargs)
diff --git a/Tools/c-analyzer/c_globals/find.py b/Tools/c-analyzer/c_globals/find.py
new file mode 100644 (file)
index 0000000..a51b947
--- /dev/null
@@ -0,0 +1,95 @@
+from c_analyzer_common import SOURCE_DIRS
+from c_analyzer_common.info import UNKNOWN
+from c_symbols import (
+        info as s_info,
+        binary as b_symbols,
+        source as s_symbols,
+        resolve,
+        )
+from c_parser import info, declarations
+
+
+# XXX needs tests:
+# * iter_variables
+
+def globals_from_binary(binfile=b_symbols.PYTHON, *,
+                        knownvars=None,
+                        dirnames=None,
+                        _iter_symbols=b_symbols.iter_symbols,
+                        _resolve=resolve.symbols_to_variables,
+                        _get_symbol_resolver=resolve.get_resolver,
+                        ):
+    """Yield a Variable for each found Symbol.
+
+    Details are filled in from the given "known" variables and types.
+    """
+    symbols = _iter_symbols(binfile, find_local_symbol=None)
+    #symbols = list(symbols)
+    for variable in _resolve(symbols,
+                             resolve=_get_symbol_resolver(knownvars, dirnames),
+                             ):
+        # Skip each non-global variable (unless we couldn't find it).
+        # XXX Drop the "UNKNOWN" condition?
+        if not variable.isglobal and variable.vartype != UNKNOWN:
+            continue
+        yield variable
+
+
+def globals_from_declarations(dirnames=SOURCE_DIRS, *,
+                              known=None,
+                              ):
+    """Yield a Variable for each found declaration.
+
+    Details are filled in from the given "known" variables and types.
+    """
+    raise NotImplementedError
+
+
+def iter_variables(kind='platform', *,
+                   known=None,
+                   dirnames=None,
+                   _resolve_symbols=resolve.symbols_to_variables,
+                   _get_symbol_resolver=resolve.get_resolver,
+                   _symbols_from_binary=b_symbols.iter_symbols,
+                   _symbols_from_source=s_symbols.iter_symbols,
+                   _iter_raw=declarations.iter_all,
+                   _iter_preprocessed=declarations.iter_preprocessed,
+                   ):
+    """Yield a Variable for each one found (e.g. in files)."""
+    kind = kind or 'platform'
+
+    if kind == 'symbols':
+        knownvars = (known or {}).get('variables')
+        yield from _resolve_symbols(
+                _symbols_from_source(dirnames, known),
+                resolve=_get_symbol_resolver(knownvars, dirnames),
+                )
+    elif kind == 'platform':
+        knownvars = (known or {}).get('variables')
+        yield from _resolve_symbols(
+                _symbols_from_binary(find_local_symbol=None),
+                resolve=_get_symbol_resolver(knownvars, dirnames),
+                )
+    elif kind == 'declarations':
+        for decl in _iter_raw(dirnames):
+            if not isinstance(decl, info.Variable):
+                continue
+            yield decl
+    elif kind == 'preprocessed':
+        for decl in _iter_preprocessed(dirnames):
+            if not isinstance(decl, info.Variable):
+                continue
+            yield decl
+    else:
+        raise ValueError(f'unsupported kind {kind!r}')
+
+
+def globals(dirnames, known, *,
+            kind=None,  # Use the default.
+            _iter_variables=iter_variables,
+            ):
+    """Return a list of (StaticVar, <supported>) for each found global var."""
+    for found in _iter_variables(kind, known=known, dirnames=dirnames):
+        if not found.isglobal:
+            continue
+        yield found
diff --git a/Tools/c-analyzer/c_globals/show.py b/Tools/c-analyzer/c_globals/show.py
new file mode 100644 (file)
index 0000000..f4298b1
--- /dev/null
@@ -0,0 +1,16 @@
+
+def basic(globals, *,
+          _print=print):
+    """Print each row simply."""
+    for variable in globals:
+        if variable.funcname:
+            line = f'{variable.filename}:{variable.funcname}():{variable.name}'
+        else:
+            line = f'{variable.filename}:{variable.name}'
+        vartype = variable.vartype
+        #if vartype.startswith('static '):
+        #    vartype = vartype.partition(' ')[2]
+        #else:
+        #    vartype = '=' + vartype
+        line = f'{line:<64} {vartype}'
+        _print(line)
diff --git a/Tools/c-analyzer/c_globals/supported.py b/Tools/c-analyzer/c_globals/supported.py
new file mode 100644 (file)
index 0000000..4643e4e
--- /dev/null
@@ -0,0 +1,368 @@
+import os.path
+import re
+
+from c_analyzer_common import DATA_DIR
+from c_analyzer_common.info import ID
+from c_analyzer_common.util import read_tsv, write_tsv
+
+
+IGNORED_FILE = os.path.join(DATA_DIR, 'ignored.tsv')
+
+IGNORED_COLUMNS = ('filename', 'funcname', 'name', 'kind', 'reason')
+IGNORED_HEADER = '\t'.join(IGNORED_COLUMNS)
+
+# XXX Move these to ignored.tsv.
+IGNORED = {
+        # global
+        'PyImport_FrozenModules': 'process-global',
+        'M___hello__': 'process-global',
+        'inittab_copy': 'process-global',
+        'PyHash_Func': 'process-global',
+        '_Py_HashSecret_Initialized': 'process-global',
+        '_TARGET_LOCALES': 'process-global',
+
+        # startup (only changed before/during)
+        '_PyRuntime': 'runtime startup',
+        'runtime_initialized': 'runtime startup',
+        'static_arg_parsers': 'runtime startup',
+        'orig_argv': 'runtime startup',
+        'opt_ptr': 'runtime startup',
+        '_preinit_warnoptions': 'runtime startup',
+        '_Py_StandardStreamEncoding': 'runtime startup',
+        'Py_FileSystemDefaultEncoding': 'runtime startup',
+        '_Py_StandardStreamErrors': 'runtime startup',
+        'Py_FileSystemDefaultEncodeErrors': 'runtime startup',
+        'Py_BytesWarningFlag': 'runtime startup',
+        'Py_DebugFlag': 'runtime startup',
+        'Py_DontWriteBytecodeFlag': 'runtime startup',
+        'Py_FrozenFlag': 'runtime startup',
+        'Py_HashRandomizationFlag': 'runtime startup',
+        'Py_IgnoreEnvironmentFlag': 'runtime startup',
+        'Py_InspectFlag': 'runtime startup',
+        'Py_InteractiveFlag': 'runtime startup',
+        'Py_IsolatedFlag': 'runtime startup',
+        'Py_NoSiteFlag': 'runtime startup',
+        'Py_NoUserSiteDirectory': 'runtime startup',
+        'Py_OptimizeFlag': 'runtime startup',
+        'Py_QuietFlag': 'runtime startup',
+        'Py_UTF8Mode': 'runtime startup',
+        'Py_UnbufferedStdioFlag': 'runtime startup',
+        'Py_VerboseFlag': 'runtime startup',
+        '_Py_path_config': 'runtime startup',
+        '_PyOS_optarg': 'runtime startup',
+        '_PyOS_opterr': 'runtime startup',
+        '_PyOS_optind': 'runtime startup',
+        '_Py_HashSecret': 'runtime startup',
+
+        # REPL
+        '_PyOS_ReadlineLock': 'repl',
+        '_PyOS_ReadlineTState': 'repl',
+
+        # effectively const
+        'tracemalloc_empty_traceback': 'const',
+        '_empty_bitmap_node': 'const',
+        'posix_constants_pathconf': 'const',
+        'posix_constants_confstr': 'const',
+        'posix_constants_sysconf': 'const',
+        '_PySys_ImplCacheTag': 'const',
+        '_PySys_ImplName': 'const',
+        'PyImport_Inittab': 'const',
+        '_PyImport_DynLoadFiletab': 'const',
+        '_PyParser_Grammar': 'const',
+        'Py_hexdigits': 'const',
+        '_PyImport_Inittab': 'const',
+        '_PyByteArray_empty_string': 'const',
+        '_PyLong_DigitValue': 'const',
+        '_Py_SwappedOp': 'const',
+        'PyStructSequence_UnnamedField': 'const',
+
+        # signals are main-thread only
+        'faulthandler_handlers': 'signals are main-thread only',
+        'user_signals': 'signals are main-thread only',
+        'wakeup': 'signals are main-thread only',
+
+        # hacks
+        '_PySet_Dummy': 'only used as a placeholder',
+        }
+
+BENIGN = 'races here are benign and unlikely'
+
+
+def is_supported(variable, ignored=None, known=None, *,
+                 _ignored=(lambda *a, **k: _is_ignored(*a, **k)),
+                 _vartype_okay=(lambda *a, **k: _is_vartype_okay(*a, **k)),
+                 ):
+    """Return True if the given global variable is okay in CPython."""
+    if _ignored(variable,
+                ignored and ignored.get('variables')):
+        return True
+    elif _vartype_okay(variable.vartype,
+                       ignored.get('types')):
+        return True
+    else:
+        return False
+
+
+def _is_ignored(variable, ignoredvars=None, *,
+                _IGNORED=IGNORED,
+                ):
+    """Return the reason if the variable is a supported global.
+
+    Return None if the variable is not a supported global.
+    """
+    if ignoredvars and (reason := ignoredvars.get(variable.id)):
+        return reason
+
+    if variable.funcname is None:
+        if reason := _IGNORED.get(variable.name):
+            return reason
+
+    # compiler
+    if variable.filename == 'Python/graminit.c':
+        if variable.vartype.startswith('static state '):
+            return 'compiler'
+    if variable.filename == 'Python/symtable.c':
+        if variable.vartype.startswith('static identifier '):
+            return 'compiler'
+    if variable.filename == 'Python/Python-ast.c':
+        # These should be const.
+        if variable.name.endswith('_field'):
+            return 'compiler'
+        if variable.name.endswith('_attribute'):
+            return 'compiler'
+
+    # other
+    if variable.filename == 'Python/dtoa.c':
+        # guarded by lock?
+        if variable.name in ('p5s', 'freelist'):
+            return 'dtoa is thread-safe?'
+        if variable.name in ('private_mem', 'pmem_next'):
+            return 'dtoa is thread-safe?'
+    if variable.filename == 'Python/thread.c':
+        # Threads do not become an issue until after these have been set
+        # and these never get changed after that.
+        if variable.name in ('initialized', 'thread_debug'):
+            return 'thread-safe'
+    if variable.filename == 'Python/getversion.c':
+        if variable.name == 'version':
+            # Races are benign here, as well as unlikely.
+            return BENIGN
+    if variable.filename == 'Python/fileutils.c':
+        if variable.name == 'force_ascii':
+            return BENIGN
+        if variable.name == 'ioctl_works':
+            return BENIGN
+        if variable.name == '_Py_open_cloexec_works':
+            return BENIGN
+    if variable.filename == 'Python/codecs.c':
+        if variable.name == 'ucnhash_CAPI':
+            return BENIGN
+    if variable.filename == 'Python/bootstrap_hash.c':
+        if variable.name == 'getrandom_works':
+            return BENIGN
+    if variable.filename == 'Objects/unicodeobject.c':
+        if variable.name == 'ucnhash_CAPI':
+            return BENIGN
+        if variable.name == 'bloom_linebreak':
+            # *mostly* benign
+            return BENIGN
+    if variable.filename == 'Modules/getbuildinfo.c':
+        if variable.name == 'buildinfo':
+            # The static is used for pre-allocation.
+            return BENIGN
+    if variable.filename == 'Modules/posixmodule.c':
+        if variable.name == 'ticks_per_second':
+            return BENIGN
+        if variable.name == 'dup3_works':
+            return BENIGN
+    if variable.filename == 'Modules/timemodule.c':
+        if variable.name == 'ticks_per_second':
+            return BENIGN
+    if variable.filename == 'Objects/longobject.c':
+        if variable.name == 'log_base_BASE':
+            return BENIGN
+        if variable.name == 'convwidth_base':
+            return BENIGN
+        if variable.name == 'convmultmax_base':
+            return BENIGN
+
+    return None
+
+
+def _is_vartype_okay(vartype, ignoredtypes=None):
+    if _is_object(vartype):
+        return None
+
+    if vartype.startswith('static const '):
+        return 'const'
+    if vartype.startswith('const '):
+        return 'const'
+
+    # components for TypeObject definitions
+    for name in ('PyMethodDef', 'PyGetSetDef', 'PyMemberDef'):
+        if name in vartype:
+            return 'const'
+    for name in ('PyNumberMethods', 'PySequenceMethods', 'PyMappingMethods',
+                 'PyBufferProcs', 'PyAsyncMethods'):
+        if name in vartype:
+            return 'const'
+    for name in ('slotdef', 'newfunc'):
+        if name in vartype:
+            return 'const'
+
+    # structseq
+    for name in ('PyStructSequence_Desc', 'PyStructSequence_Field'):
+        if name in vartype:
+            return 'const'
+
+    # other definiitions
+    if 'PyModuleDef' in vartype:
+        return 'const'
+
+    # thread-safe
+    if '_Py_atomic_int' in vartype:
+        return 'thread-safe'
+    if 'pthread_condattr_t' in vartype:
+        return 'thread-safe'
+
+    # startup
+    if '_Py_PreInitEntry' in vartype:
+        return 'startup'
+
+    # global
+#    if 'PyMemAllocatorEx' in vartype:
+#        return True
+
+    # others
+#    if 'PyThread_type_lock' in vartype:
+#        return True
+
+    # XXX ???
+    # _Py_tss_t
+    # _Py_hashtable_t
+    # stack_t
+    # _PyUnicode_Name_CAPI
+
+    # functions
+    if '(' in vartype and '[' not in vartype:
+        return 'function pointer'
+
+    # XXX finish!
+    # * allow const values?
+    #raise NotImplementedError
+    return None
+
+
+def _is_object(vartype):
+    if re.match(r'.*\bPy\w*Object\b', vartype):
+        return True
+    if '_PyArg_Parser ' in vartype:
+        return True
+    if vartype.startswith(('_Py_IDENTIFIER(', 'static _Py_Identifier',
+                           '_Py_static_string(')):
+        return True
+    if 'traceback_t' in vartype:
+        return True
+    if 'PyAsyncGenASend' in vartype:
+        return True
+    if '_PyAsyncGenWrappedValue' in vartype:
+        return True
+    if 'PyContext' in vartype:
+        return True
+    if 'method_cache_entry' in vartype:
+        return True
+    if vartype.startswith('static identifier '):
+        return True
+    if vartype.endswith((' _Py_FalseStruct', ' _Py_TrueStruct')):
+        return True
+
+    # XXX Add more?
+
+    #for part in vartype.split():
+    #    # XXX const is automatic True?
+    #    if part == 'PyObject' or part.startswith('PyObject['):
+    #        return True
+    return False
+
+
+def ignored_from_file(infile, *,
+                      _read_tsv=read_tsv,
+                      ):
+    """Yield a Variable for each ignored var in the file."""
+    ignored = {
+        'variables': {},
+        #'types': {},
+        #'constants': {},
+        #'macros': {},
+        }
+    for row in _read_tsv(infile, IGNORED_HEADER):
+        filename, funcname, name, kind, reason = row
+        if not funcname or funcname == '-':
+            funcname = None
+        id = ID(filename, funcname, name)
+        if kind == 'variable':
+            values = ignored['variables']
+        else:
+            raise ValueError(f'unsupported kind in row {row}')
+        values[id] = reason
+    return ignored
+
+
+##################################
+# generate
+
+def _get_row(varid, reason):
+    return (
+            varid.filename,
+            varid.funcname or '-',
+            varid.name,
+            'variable',
+            str(reason),
+            )
+
+
+def _get_rows(variables, ignored=None, *,
+              _as_row=_get_row,
+              _is_ignored=_is_ignored,
+              _vartype_okay=_is_vartype_okay,
+              ):
+    count = 0
+    for variable in variables:
+        reason = _is_ignored(variable,
+                             ignored and ignored.get('variables'),
+                             )
+        if not reason:
+            reason = _vartype_okay(variable.vartype,
+                                   ignored and ignored.get('types'))
+        if not reason:
+            continue
+
+        print(' ', variable, repr(reason))
+        yield _as_row(variable.id, reason)
+        count += 1
+    print(f'total: {count}')
+
+
+def _generate_ignored_file(variables, filename=None, *,
+                           _generate_rows=_get_rows,
+                           _write_tsv=write_tsv,
+                           ):
+    if not filename:
+        filename = IGNORED_FILE + '.new'
+    rows = _generate_rows(variables)
+    _write_tsv(filename, IGNORED_HEADER, rows)
+
+
+if __name__ == '__main__':
+    from c_analyzer_common import SOURCE_DIRS
+    from c_analyzer_common.known import (
+        from_file as known_from_file,
+        DATA_FILE as KNOWN_FILE,
+        )
+    from . import find
+    known = known_from_file(KNOWN_FILE)
+    knownvars = (known or {}).get('variables')
+    variables = find.globals_from_binary(knownvars=knownvars,
+                                         dirnames=SOURCE_DIRS)
+
+    _generate_ignored_file(variables)
diff --git a/Tools/c-analyzer/c_parser/__init__.py b/Tools/c-analyzer/c_parser/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tools/c-analyzer/c_parser/declarations.py b/Tools/c-analyzer/c_parser/declarations.py
new file mode 100644 (file)
index 0000000..19fa3ff
--- /dev/null
@@ -0,0 +1,295 @@
+import re
+import shlex
+import subprocess
+
+from . import source
+
+
+IDENTIFIER = r'(?:[a-zA-z]|_+[a-zA-Z0-9]\w*)'
+
+TYPE_QUAL = r'(?:const|volatile)'
+
+VAR_TYPE_SPEC = r'''(?:
+        void |
+        (?:
+         (?:(?:un)?signed\s+)?
+         (?:
+          char |
+          short |
+          int |
+          long |
+          long\s+int |
+          long\s+long
+          ) |
+         ) |
+        float |
+        double |
+        {IDENTIFIER} |
+        (?:struct|union)\s+{IDENTIFIER}
+        )'''
+
+POINTER = rf'''(?:
+        (?:\s+const)?\s*[*]
+        )'''
+
+#STRUCT = r'''(?:
+#        (?:struct|(struct\s+%s))\s*[{]
+#            [^}]*
+#        [}]
+#        )''' % (IDENTIFIER)
+#UNION = r'''(?:
+#        (?:union|(union\s+%s))\s*[{]
+#            [^}]*
+#        [}]
+#        )''' % (IDENTIFIER)
+#DECL_SPEC = rf'''(?:
+#        ({VAR_TYPE_SPEC}) |
+#        ({STRUCT}) |
+#        ({UNION})
+#        )'''
+
+FUNC_START = rf'''(?:
+        (?:
+          (?:
+            extern |
+            static |
+            static\s+inline
+           )\s+
+         )?
+        #(?:const\s+)?
+        {VAR_TYPE_SPEC}
+        )'''
+#GLOBAL_VAR_START = rf'''(?:
+#        (?:
+#          (?:
+#            extern |
+#            static
+#           )\s+
+#         )?
+#        (?:
+#           {TYPE_QUAL}
+#           (?:\s+{TYPE_QUAL})?
+#         )?\s+
+#        {VAR_TYPE_SPEC}
+#        )'''
+GLOBAL_DECL_START_RE = re.compile(rf'''
+        ^
+        (?:
+            ({FUNC_START})
+         )
+        ''', re.VERBOSE)
+
+LOCAL_VAR_START = rf'''(?:
+        (?:
+          (?:
+            register |
+            static
+           )\s+
+         )?
+        (?:
+          (?:
+            {TYPE_QUAL}
+            (?:\s+{TYPE_QUAL})?
+           )\s+
+         )?
+        {VAR_TYPE_SPEC}
+        {POINTER}?
+        )'''
+LOCAL_STMT_START_RE = re.compile(rf'''
+        ^
+        (?:
+            ({LOCAL_VAR_START})
+         )
+        ''', re.VERBOSE)
+
+
+def iter_global_declarations(lines):
+    """Yield (decl, body) for each global declaration in the given lines.
+
+    For function definitions the header is reduced to one line and
+    the body is provided as-is.  For other compound declarations (e.g.
+    struct) the entire declaration is reduced to one line and "body"
+    is None.  Likewise for simple declarations (e.g. variables).
+
+    Declarations inside function bodies are ignored, though their text
+    is provided in the function body.
+    """
+    # XXX Bail out upon bogus syntax.
+    lines = source.iter_clean_lines(lines)
+    for line in lines:
+        if not GLOBAL_DECL_START_RE.match(line):
+            continue
+        # We only need functions here, since we only need locals for now.
+        if line.endswith(';'):
+            continue
+        if line.endswith('{') and '(' not in line:
+            continue
+
+        # Capture the function.
+        # (assume no func is a one-liner)
+        decl = line
+        while '{' not in line:  # assume no inline structs, etc.
+            try:
+                line = next(lines)
+            except StopIteration:
+                return
+            decl += ' ' + line
+
+        body, end = _extract_block(lines)
+        if end is None:
+            return
+        assert end == '}'
+        yield (f'{decl}\n{body}\n{end}', body)
+
+
+def iter_local_statements(lines):
+    """Yield (lines, blocks) for each statement in the given lines.
+
+    For simple statements, "blocks" is None and the statement is reduced
+    to a single line.  For compound statements, "blocks" is a pair of
+    (header, body) for each block in the statement.  The headers are
+    reduced to a single line each, but the bpdies are provided as-is.
+    """
+    # XXX Bail out upon bogus syntax.
+    lines = source.iter_clean_lines(lines)
+    for line in lines:
+        if not LOCAL_STMT_START_RE.match(line):
+            continue
+
+        stmt = line
+        blocks = None
+        if not line.endswith(';'):
+            # XXX Support compound & multiline simple statements.
+            #blocks = []
+            continue
+
+        yield (stmt, blocks)
+
+
+def _extract_block(lines):
+    end = None
+    depth = 1
+    body = []
+    for line in lines:
+        depth += line.count('{') - line.count('}')
+        if depth == 0:
+            end = line
+            break
+        body.append(line)
+    return '\n'.join(body), end
+
+
+def parse_func(stmt, body):
+    """Return (name, signature) for the given function definition."""
+    header, _, end = stmt.partition(body)
+    assert end.strip() == '}'
+    assert header.strip().endswith('{')
+    header, _, _= header.rpartition('{')
+
+    signature = ' '.join(header.strip().splitlines())
+
+    _, _, name = signature.split('(')[0].strip().rpartition(' ')
+    assert name
+
+    return name, signature
+
+
+def parse_var(stmt):
+    """Return (name, vartype) for the given variable declaration."""
+    stmt = stmt.rstrip(';')
+    m = LOCAL_STMT_START_RE.match(stmt)
+    assert m
+    vartype = m.group(0)
+    name = stmt[len(vartype):].partition('=')[0].strip()
+
+    if name.startswith('('):
+        name, _, after = name[1:].partition(')')
+        assert after
+        name = name.replace('*', '* ')
+        inside, _, name = name.strip().rpartition(' ')
+        vartype = f'{vartype} ({inside.strip()}){after}'
+    else:
+        name = name.replace('*', '* ')
+        before, _, name = name.rpartition(' ')
+        vartype = f'{vartype} {before}'
+
+    vartype = vartype.strip()
+    while '  ' in vartype:
+        vartype = vartype.replace('  ', ' ')
+
+    return name, vartype
+
+
+def parse_compound(stmt, blocks):
+    """Return (headers, bodies) for the given compound statement."""
+    # XXX Identify declarations inside compound statements
+    # (if/switch/for/while).
+    raise NotImplementedError
+
+
+def iter_variables(filename, *,
+                   _iter_source_lines=source.iter_lines,
+                   _iter_global=iter_global_declarations,
+                   _iter_local=iter_local_statements,
+                   _parse_func=parse_func,
+                   _parse_var=parse_var,
+                   _parse_compound=parse_compound,
+                   ):
+    """Yield (funcname, name, vartype) for every variable in the given file."""
+    lines = _iter_source_lines(filename)
+    for stmt, body in _iter_global(lines):
+        # At the file top-level we only have to worry about vars & funcs.
+        if not body:
+            name, vartype = _parse_var(stmt)
+            if name:
+                yield (None, name, vartype)
+        else:
+            funcname, _ = _parse_func(stmt, body)
+            localvars = _iter_locals(body,
+                                     _iter_statements=_iter_local,
+                                     _parse_var=_parse_var,
+                                     _parse_compound=_parse_compound,
+                                     )
+            for name, vartype in localvars:
+                yield (funcname, name, vartype)
+
+
+def _iter_locals(lines, *,
+                 _iter_statements=iter_local_statements,
+                 _parse_var=parse_var,
+                 _parse_compound=parse_compound,
+                 ):
+    compound = [lines]
+    while compound:
+        body = compound.pop(0)
+        bodylines = body.splitlines()
+        for stmt, blocks in _iter_statements(bodylines):
+            if not blocks:
+                name, vartype = _parse_var(stmt)
+                if name:
+                    yield (name, vartype)
+            else:
+                headers, bodies = _parse_compound(stmt, blocks)
+                for header in headers:
+                    for line in header:
+                        name, vartype = _parse_var(line)
+                        if name:
+                            yield (name, vartype)
+                compound.extend(bodies)
+
+
+def iter_all(dirnames):
+    """Yield a Declaration for each one found.
+
+    If there are duplicates, due to preprocessor conditionals, then
+    they are checked to make sure they are the same.
+    """
+    raise NotImplementedError
+
+
+def iter_preprocessed(dirnames):
+    """Yield a Declaration for each one found.
+
+    All source files are run through the preprocessor first.
+    """
+    raise NotImplementedError
diff --git a/Tools/c-analyzer/c_parser/info.py b/Tools/c-analyzer/c_parser/info.py
new file mode 100644 (file)
index 0000000..9ab6979
--- /dev/null
@@ -0,0 +1,78 @@
+from collections import namedtuple
+
+from c_analyzer_common import info, util
+from c_analyzer_common.util import classonly, _NTBase
+
+
+def normalize_vartype(vartype):
+    """Return the canonical form for a variable type (or func signature)."""
+    # We allow empty strring through for semantic reasons.
+    if vartype is None:
+        return None
+
+    # XXX finish!
+    # XXX Return (modifiers, type, pointer)?
+    return str(vartype)
+
+
+class Variable(_NTBase,
+               namedtuple('Variable', 'id vartype')):
+    """Information about a single variable declaration."""
+
+    __slots__ = ()
+    _isglobal = util.Slot()
+
+    @classonly
+    def from_parts(cls, filename, funcname, name, vartype, isglobal=False):
+        id = info.ID(filename, funcname, name)
+        self = cls(id, vartype)
+        if isglobal:
+            self._isglobal = True
+        return self
+
+    def __new__(cls, id, vartype):
+        self = super().__new__(
+                cls,
+                id=info.ID.from_raw(id),
+                vartype=normalize_vartype(vartype) if vartype else None,
+                )
+        return self
+
+    def __hash__(self):
+        return hash(self.id)
+
+    def __getattr__(self, name):
+        return getattr(self.id, name)
+
+    def _validate_id(self):
+        if not self.id:
+            raise TypeError('missing id')
+
+        if not self.filename or self.filename == info.UNKNOWN:
+            raise TypeError(f'id missing filename ({self.id})')
+
+        if self.funcname and self.funcname == info.UNKNOWN:
+            raise TypeError(f'id missing funcname ({self.id})')
+
+        self.id.validate()
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        self._validate_id()
+
+        if self.vartype is None or self.vartype == info.UNKNOWN:
+            raise TypeError('missing vartype')
+
+    @property
+    def isglobal(self):
+        try:
+            return self._isglobal
+        except AttributeError:
+            # XXX Include extern variables.
+            # XXX Ignore functions.
+            self._isglobal = ('static' in self.vartype.split())
+            return self._isglobal
+
+    @property
+    def isconst(self):
+        return 'const' in self.vartype.split()
diff --git a/Tools/c-analyzer/c_parser/naive.py b/Tools/c-analyzer/c_parser/naive.py
new file mode 100644 (file)
index 0000000..e0370cc
--- /dev/null
@@ -0,0 +1,180 @@
+import re
+
+from c_analyzer_common.info import UNKNOWN
+
+from .info import Variable
+from .preprocessor import _iter_clean_lines
+
+
+_NOT_SET = object()
+
+
+def get_srclines(filename, *,
+                 cache=None,
+                 _open=open,
+                 _iter_lines=_iter_clean_lines,
+                 ):
+    """Return the file's lines as a list.
+
+    Each line will have trailing whitespace removed (including newline).
+
+    If a cache is given the it is used.
+    """
+    if cache is not None:
+        try:
+            return cache[filename]
+        except KeyError:
+            pass
+
+    with _open(filename) as srcfile:
+        srclines = [line
+                    for _, line in _iter_lines(srcfile)
+                    if not line.startswith('#')]
+    for i, line in enumerate(srclines):
+        srclines[i] = line.rstrip()
+
+    if cache is not None:
+        cache[filename] = srclines
+    return srclines
+
+
+def parse_variable_declaration(srcline):
+    """Return (name, decl) for the given declaration line."""
+    # XXX possible false negatives...
+    decl, sep, _ = srcline.partition('=')
+    if not sep:
+        if not srcline.endswith(';'):
+            return None, None
+        decl = decl.strip(';')
+    decl = decl.strip()
+    m = re.match(r'.*\b(\w+)\s*(?:\[[^\]]*\])?$', decl)
+    if not m:
+        return None, None
+    name = m.group(1)
+    return name, decl
+
+
+def parse_variable(srcline, funcname=None):
+    """Return a Variable for the variable declared on the line (or None)."""
+    line = srcline.strip()
+
+    # XXX Handle more than just static variables.
+    if line.startswith('static '):
+        if '(' in line and '[' not in line:
+            # a function
+            return None, None
+        return parse_variable_declaration(line)
+    else:
+        return None, None
+
+
+def iter_variables(filename, *,
+                   srccache=None,
+                   parse_variable=None,
+                   _get_srclines=get_srclines,
+                   _default_parse_variable=parse_variable,
+                   ):
+    """Yield a Variable for each in the given source file."""
+    if parse_variable is None:
+        parse_variable = _default_parse_variable
+
+    indent = ''
+    prev = ''
+    funcname = None
+    for line in _get_srclines(filename, cache=srccache):
+        # remember current funcname
+        if funcname:
+            if line == indent + '}':
+                funcname = None
+                continue
+        else:
+            if '(' in prev and line == indent + '{':
+                if not prev.startswith('__attribute__'):
+                    funcname = prev.split('(')[0].split()[-1]
+                    prev = ''
+                    continue
+            indent = line[:-len(line.lstrip())]
+            prev = line
+
+        info = parse_variable(line, funcname)
+        if isinstance(info, list):
+            for name, _funcname, decl in info:
+                yield Variable.from_parts(filename, _funcname, name, decl)
+            continue
+        name, decl = info
+
+        if name is None:
+            continue
+        yield Variable.from_parts(filename, funcname, name, decl)
+
+
+def _match_varid(variable, name, funcname, ignored=None):
+    if ignored and variable in ignored:
+        return False
+
+    if variable.name != name:
+        return False
+
+    if funcname == UNKNOWN:
+        if not variable.funcname:
+            return False
+    elif variable.funcname != funcname:
+        return False
+
+    return True
+
+
+def find_variable(filename, funcname, name, *,
+                  ignored=None,
+                  srccache=None,  # {filename: lines}
+                  parse_variable=None,
+                  _iter_variables=iter_variables,
+                  ):
+    """Return the matching variable.
+
+    Return None if the variable is not found.
+    """
+    for variable in _iter_variables(filename,
+                                    srccache=srccache,
+                                    parse_variable=parse_variable,
+                                    ):
+        if _match_varid(variable, name, funcname, ignored):
+            return variable
+    else:
+        return None
+
+
+def find_variables(varids, filenames=None, *,
+                   srccache=_NOT_SET,
+                   parse_variable=None,
+                   _find_symbol=find_variable,
+                   ):
+    """Yield a Variable for each ID.
+
+    If the variable is not found then its decl will be UNKNOWN.  That
+    way there will be one resulting Variable per given ID.
+    """
+    if srccache is _NOT_SET:
+        srccache = {}
+
+    used = set()
+    for varid in varids:
+        if varid.filename and varid.filename != UNKNOWN:
+            srcfiles = [varid.filename]
+        else:
+            if not filenames:
+                yield Variable(varid, UNKNOWN)
+                continue
+            srcfiles = filenames
+        for filename in srcfiles:
+            found = _find_varid(filename, varid.funcname, varid.name,
+                                 ignored=used,
+                                 srccache=srccache,
+                                 parse_variable=parse_variable,
+                                 )
+            if found:
+                yield found
+                used.add(found)
+                break
+        else:
+            yield Variable(varid, UNKNOWN)
diff --git a/Tools/c-analyzer/c_parser/preprocessor.py b/Tools/c-analyzer/c_parser/preprocessor.py
new file mode 100644 (file)
index 0000000..0e2866e
--- /dev/null
@@ -0,0 +1,512 @@
+from collections import namedtuple
+import shlex
+import os
+import re
+
+from c_analyzer_common import util
+from . import info
+
+
+CONTINUATION = '\\' + os.linesep
+
+IDENTIFIER = r'(?:\w*[a-zA-Z]\w*)'
+IDENTIFIER_RE = re.compile('^' + IDENTIFIER + '$')
+
+
+def _coerce_str(value):
+    if not value:
+        return ''
+    return str(value).strip()
+
+
+#############################
+# directives
+
+DIRECTIVE_START = r'''
+    (?:
+      ^ \s*
+      [#] \s*
+      )'''
+DIRECTIVE_TEXT = r'''
+    (?:
+      (?: \s+ ( .*\S ) )?
+      \s* $
+      )'''
+DIRECTIVE = rf'''
+    (?:
+      {DIRECTIVE_START}
+      (
+        include |
+        error | warning |
+        pragma |
+        define | undef |
+        if | ifdef | ifndef | elseif | else | endif |
+        __FILE__ | __LINE__ | __DATE __ | __TIME__ | __TIMESTAMP__
+        )
+      {DIRECTIVE_TEXT}
+      )'''
+#       (?:
+#        [^\\\n] |
+#        \\ [^\n] |
+#        \\ \n
+#        )+
+#      ) \n
+#     )'''
+DIRECTIVE_RE = re.compile(DIRECTIVE, re.VERBOSE)
+
+DEFINE = rf'''
+    (?:
+      {DIRECTIVE_START} define \s+
+      (?:
+        ( \w*[a-zA-Z]\w* )
+        (?: \s* [(] ([^)]*) [)] )?
+        )
+      {DIRECTIVE_TEXT}
+      )'''
+DEFINE_RE = re.compile(DEFINE, re.VERBOSE)
+
+
+def parse_directive(line):
+    """Return the appropriate directive for the given line."""
+    line = line.strip()
+    if line.startswith('#'):
+        line = line[1:].lstrip()
+        line = '#' + line
+    directive = line
+    #directive = '#' + line
+    while '  ' in directive:
+        directive = directive.replace('  ', ' ')
+    return _parse_directive(directive)
+
+
+def _parse_directive(line):
+    m = DEFINE_RE.match(line)
+    if m:
+        name, args, text = m.groups()
+        if args:
+            args = [a.strip() for a in args.split(',')]
+            return Macro(name, args, text)
+        else:
+            return Constant(name, text)
+
+    m = DIRECTIVE_RE.match(line)
+    if not m:
+        raise ValueError(f'unsupported directive {line!r}')
+    kind, text = m.groups()
+    if not text:
+        if kind not in ('else', 'endif'):
+            raise ValueError(f'missing text in directive {line!r}')
+    elif kind in ('else', 'endif', 'define'):
+        raise ValueError(f'unexpected text in directive {line!r}')
+    if kind == 'include':
+        directive = Include(text)
+    elif kind in IfDirective.KINDS:
+        directive = IfDirective(kind, text)
+    else:
+        directive = OtherDirective(kind, text)
+    directive.validate()
+    return directive
+
+
+class PreprocessorDirective(util._NTBase):
+    """The base class for directives."""
+
+    __slots__ = ()
+
+    KINDS = frozenset([
+            'include',
+            'pragma',
+            'error', 'warning',
+            'define', 'undef',
+            'if', 'ifdef', 'ifndef', 'elseif', 'else', 'endif',
+            '__FILE__', '__DATE__', '__LINE__', '__TIME__', '__TIMESTAMP__',
+            ])
+
+    @property
+    def text(self):
+        return ' '.join(v for v in self[1:] if v and v.strip()) or None
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if not self.kind:
+            raise TypeError('missing kind')
+        elif self.kind not in self.KINDS:
+            raise ValueError
+
+        # text can be anything, including None.
+
+
+class Constant(PreprocessorDirective,
+               namedtuple('Constant', 'kind name value')):
+    """A single "constant" directive ("define")."""
+
+    __slots__ = ()
+
+    def __new__(cls, name, value=None):
+        self = super().__new__(
+                cls,
+                'define',
+                name=_coerce_str(name) or None,
+                value=_coerce_str(value) or None,
+                )
+        return self
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if not self.name:
+            raise TypeError('missing name')
+        elif not IDENTIFIER_RE.match(self.name):
+            raise ValueError(f'name must be identifier, got {self.name!r}')
+
+        # value can be anything, including None
+
+
+class Macro(PreprocessorDirective,
+            namedtuple('Macro', 'kind name args body')):
+    """A single "macro" directive ("define")."""
+
+    __slots__ = ()
+
+    def __new__(cls, name, args, body=None):
+        # "args" must be a string or an iterable of strings (or "empty").
+        if isinstance(args, str):
+            args = [v.strip() for v in args.split(',')]
+        if args:
+            args = tuple(_coerce_str(a) or None for a in args)
+        self = super().__new__(
+                cls,
+                kind='define',
+                name=_coerce_str(name) or None,
+                args=args if args else (),
+                body=_coerce_str(body) or None,
+                )
+        return self
+
+    @property
+    def text(self):
+        if self.body:
+            return f'{self.name}({", ".join(self.args)}) {self.body}'
+        else:
+            return f'{self.name}({", ".join(self.args)})'
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if not self.name:
+            raise TypeError('missing name')
+        elif not IDENTIFIER_RE.match(self.name):
+            raise ValueError(f'name must be identifier, got {self.name!r}')
+
+        for arg in self.args:
+            if not arg:
+                raise ValueError(f'missing arg in {self.args}')
+            elif not IDENTIFIER_RE.match(arg):
+                raise ValueError(f'arg must be identifier, got {arg!r}')
+
+        # body can be anything, including None
+
+
+class IfDirective(PreprocessorDirective,
+                  namedtuple('IfDirective', 'kind condition')):
+    """A single conditional directive (e.g. "if", "ifdef").
+
+    This only includes directives that actually provide conditions.  The
+    related directives "else" and "endif" are covered by OtherDirective
+    instead.
+    """
+
+    __slots__ = ()
+
+    KINDS = frozenset([
+            'if',
+            'ifdef',
+            'ifndef',
+            'elseif',
+            ])
+
+    @classmethod
+    def _condition_from_raw(cls, raw, kind):
+        #return Condition.from_raw(raw, _kind=kind)
+        condition = _coerce_str(raw)
+        if not condition:
+            return None
+
+        if kind == 'ifdef':
+            condition = f'defined({condition})'
+        elif kind == 'ifndef':
+            condition = f'! defined({condition})'
+
+        return condition
+
+    def __new__(cls, kind, condition):
+        kind = _coerce_str(kind)
+        self = super().__new__(
+                cls,
+                kind=kind or None,
+                condition=cls._condition_from_raw(condition, kind),
+                )
+        return self
+
+    @property
+    def text(self):
+        if self.kind == 'ifdef':
+            return self.condition[8:-1]  # strip "defined("
+        elif self.kind == 'ifndef':
+            return self.condition[10:-1]  # strip "! defined("
+        else:
+            return self.condition
+        #return str(self.condition)
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if not self.condition:
+            raise TypeError('missing condition')
+        #else:
+        #    for cond in self.condition:
+        #        if not cond:
+        #            raise ValueError(f'missing condition in {self.condition}')
+        #        cond.validate()
+        #    if self.kind in ('ifdef', 'ifndef'):
+        #        if len(self.condition) != 1:
+        #            raise ValueError('too many condition')
+        #        if self.kind == 'ifdef':
+        #            if not self.condition[0].startswith('defined '):
+        #                raise ValueError('bad condition')
+        #        else:
+        #            if not self.condition[0].startswith('! defined '):
+        #                raise ValueError('bad condition')
+
+
+class Include(PreprocessorDirective,
+              namedtuple('Include', 'kind file')):
+    """A single "include" directive.
+
+    Supported "file" values are either follow the bracket style
+    (<stdio>) or double quotes ("spam.h").
+    """
+
+    __slots__ = ()
+
+    def __new__(cls, file):
+        self = super().__new__(
+                cls,
+                kind='include',
+                file=_coerce_str(file) or None,
+                )
+        return self
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if not self.file:
+            raise TypeError('missing file')
+
+
+class OtherDirective(PreprocessorDirective,
+                     namedtuple('OtherDirective', 'kind text')):
+    """A single directive not covered by another class.
+
+    This includes the "else", "endif", and "undef" directives, which are
+    otherwise inherently related to the directives covered by the
+    Constant, Macro, and IfCondition classes.
+
+    Note that all directives must have a text value, except for "else"
+    and "endif" (which must have no text).
+    """
+
+    __slots__ = ()
+
+    KINDS = PreprocessorDirective.KINDS - {'include', 'define'} - IfDirective.KINDS
+
+    def __new__(cls, kind, text):
+        self = super().__new__(
+                cls,
+                kind=_coerce_str(kind) or None,
+                text=_coerce_str(text) or None,
+                )
+        return self
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        super().validate()
+
+        if self.text:
+            if self.kind in ('else', 'endif'):
+                raise ValueError('unexpected text in directive')
+        elif self.kind not in ('else', 'endif'):
+            raise TypeError('missing text')
+
+
+#############################
+# iterating lines
+
+def _recompute_conditions(directive, ifstack):
+    if directive.kind in ('if', 'ifdef', 'ifndef'):
+        ifstack.append(
+                ([], directive.condition))
+    elif directive.kind == 'elseif':
+        if ifstack:
+            negated, active = ifstack.pop()
+            if active:
+                negated.append(active)
+        else:
+            negated = []
+        ifstack.append(
+                (negated, directive.condition))
+    elif directive.kind == 'else':
+        if ifstack:
+            negated, active = ifstack.pop()
+            if active:
+                negated.append(active)
+            ifstack.append(
+                    (negated, None))
+    elif directive.kind == 'endif':
+        if ifstack:
+            ifstack.pop()
+
+    conditions = []
+    for negated, active in ifstack:
+        for condition in negated:
+            conditions.append(f'! ({condition})')
+        if active:
+            conditions.append(active)
+    return tuple(conditions)
+
+
+def _iter_clean_lines(lines):
+    lines = iter(enumerate(lines, 1))
+    for lno, line in lines:
+        # Handle line continuations.
+        while line.endswith(CONTINUATION):
+            try:
+                lno, _line = next(lines)
+            except StopIteration:
+                break
+            line = line[:-len(CONTINUATION)] + ' ' + _line
+
+        # Deal with comments.
+        after = line
+        line = ''
+        while True:
+            # Look for a comment.
+            before, begin, remainder = after.partition('/*')
+            if '//' in before:
+                before, _, _ = before.partition('//')
+                line += before + ' '  # per the C99 spec
+                break
+            line += before
+            if not begin:
+                break
+            line += ' '  # per the C99 spec
+
+            # Go until we find the end of the comment.
+            _, end, after = remainder.partition('*/')
+            while not end:
+                try:
+                    lno, remainder = next(lines)
+                except StopIteration:
+                    raise Exception('unterminated comment')
+                _, end, after = remainder.partition('*/')
+
+        yield lno, line
+
+
+def iter_lines(lines, *,
+                   _iter_clean_lines=_iter_clean_lines,
+                   _parse_directive=_parse_directive,
+                   _recompute_conditions=_recompute_conditions,
+                   ):
+    """Yield (lno, line, directive, active conditions) for each given line.
+
+    This is effectively a subset of the operations taking place in
+    translation phases 2-4 from the C99 spec (ISO/IEC 9899:TC2); see
+    section 5.1.1.2.  Line continuations are removed and comments
+    replaced with a single space.  (In both cases "lno" will be the last
+    line involved.)  Otherwise each line is returned as-is.
+
+    "lno" is the (1-indexed) line number for the line.
+
+    "directive" will be a PreprocessorDirective or None, depending on
+    whether or not there is a directive on the line.
+
+    "active conditions" is the set of preprocessor conditions (e.g.
+    "defined()") under which the current line of code will be included
+    in compilation.  That set is derived from every conditional
+    directive block (e.g. "if defined()", "ifdef", "else") containing
+    that line.  That includes nested directives.  Note that the
+    current line does not affect the active conditions for iteself.
+    It only impacts subsequent lines.  That applies to directives
+    that close blocks (e.g. "endif") just as much as conditional
+    directvies.  Also note that "else" and "elseif" directives
+    update the active conditions (for later lines), rather than
+    adding to them.
+    """
+    ifstack = []
+    conditions = ()
+    for lno, line in _iter_clean_lines(lines):
+        stripped = line.strip()
+        if not stripped.startswith('#'):
+            yield lno, line, None, conditions
+            continue
+
+        directive = '#' + stripped[1:].lstrip()
+        while '  ' in directive:
+            directive = directive.replace('  ', ' ')
+        directive = _parse_directive(directive)
+        yield lno, line, directive, conditions
+
+        if directive.kind in ('else', 'endif'):
+            conditions = _recompute_conditions(directive, ifstack)
+        elif isinstance(directive, IfDirective):
+            conditions = _recompute_conditions(directive, ifstack)
+
+
+#############################
+# running (platform-specific?)
+
+def _gcc(filename, *,
+         _get_argv=(lambda: _get_gcc_argv()),
+         _run=util.run_cmd,
+         ):
+    argv = _get_argv()
+    argv.extend([
+            '-E', filename,
+            ])
+    output = _run(argv)
+    return output
+
+
+def _get_gcc_argv(*,
+                  _open=open,
+                  _run=util.run_cmd,
+                  ):
+    with _open('/tmp/print.mk', 'w') as tmpfile:
+        tmpfile.write('print-%:\n')
+        #tmpfile.write('\t@echo $* = $($*)\n')
+        tmpfile.write('\t@echo $($*)\n')
+    argv = ['/usr/bin/make',
+            '-f', 'Makefile',
+            '-f', '/tmp/print.mk',
+            'print-CC',
+            'print-PY_CORE_CFLAGS',
+            ]
+    output = _run(argv)
+    gcc, cflags = output.strip().splitlines()
+    argv = shlex.split(gcc.strip())
+    cflags = shlex.split(cflags.strip())
+    return argv + cflags
+
+
+def run(filename, *,
+        _gcc=_gcc,
+        ):
+    """Return the text of the given file after running the preprocessor."""
+    return _gcc(filename)
diff --git a/Tools/c-analyzer/c_parser/source.py b/Tools/c-analyzer/c_parser/source.py
new file mode 100644 (file)
index 0000000..f8998c8
--- /dev/null
@@ -0,0 +1,34 @@
+from . import preprocessor
+
+
+def iter_clean_lines(lines):
+    incomment = False
+    for line in lines:
+        # Deal with comments.
+        if incomment:
+            _, sep, line = line.partition('*/')
+            if sep:
+                incomment = False
+            continue
+        line, _, _ = line.partition('//')
+        line, sep, remainder = line.partition('/*')
+        if sep:
+            _, sep, after = remainder.partition('*/')
+            if not sep:
+                incomment = True
+                continue
+            line += ' ' + after
+
+        # Ignore blank lines and leading/trailing whitespace.
+        line = line.strip()
+        if not line:
+            continue
+
+        yield line
+
+
+def iter_lines(filename, *,
+               preprocess=preprocessor.run,
+               ):
+    content = preprocess(filename)
+    return iter(content.splitlines())
diff --git a/Tools/c-analyzer/c_symbols/__init__.py b/Tools/c-analyzer/c_symbols/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Tools/c-analyzer/c_symbols/binary.py b/Tools/c-analyzer/c_symbols/binary.py
new file mode 100644 (file)
index 0000000..e125dbd
--- /dev/null
@@ -0,0 +1,157 @@
+import os
+import os.path
+import shutil
+import sys
+
+from c_analyzer_common import util, info
+from . import source
+from .info import Symbol
+
+
+#PYTHON = os.path.join(REPO_ROOT, 'python')
+PYTHON = sys.executable
+
+
+def iter_symbols(binary=PYTHON, dirnames=None, *,
+                 # Alternately, use look_up_known_symbol()
+                 # from c_globals.supported.
+                 find_local_symbol=source.find_symbol,
+                 _file_exists=os.path.exists,
+                 _iter_symbols_nm=(lambda b, *a: _iter_symbols_nm(b, *a)),
+                 ):
+    """Yield a Symbol for each symbol found in the binary."""
+    if not _file_exists(binary):
+        raise Exception('executable missing (need to build it first?)')
+
+    if find_local_symbol:
+        cache = {}
+        def find_local_symbol(name, *, _find=find_local_symbol):
+            return _find(name, dirnames, _perfilecache=cache)
+    else:
+        find_local_symbol = None
+
+    if os.name == 'nt':
+        # XXX Support this.
+        raise NotImplementedError
+    else:
+        yield from _iter_symbols_nm(binary, find_local_symbol)
+
+
+#############################
+# binary format (e.g. ELF)
+
+SPECIAL_SYMBOLS = {
+        '__bss_start',
+        '__data_start',
+        '__dso_handle',
+        '_DYNAMIC',
+        '_edata',
+        '_end',
+        '__environ@@GLIBC_2.2.5',
+        '_GLOBAL_OFFSET_TABLE_',
+        '__JCR_END__',
+        '__JCR_LIST__',
+        '__TMC_END__',
+        }
+
+
+def _is_special_symbol(name):
+    if name in SPECIAL_SYMBOLS:
+        return True
+    if '@@GLIBC' in name:
+        return True
+    return False
+
+
+#############################
+# "nm"
+
+NM_KINDS = {
+        'b': Symbol.KIND.VARIABLE,  # uninitialized
+        'd': Symbol.KIND.VARIABLE,  # initialized
+        #'g': Symbol.KIND.VARIABLE,  # uninitialized
+        #'s': Symbol.KIND.VARIABLE,  # initialized
+        't': Symbol.KIND.FUNCTION,
+        }
+
+
+def _iter_symbols_nm(binary, find_local_symbol=None,
+                     *,
+                     _which=shutil.which,
+                     _run=util.run_cmd,
+                     ):
+    nm = _which('nm')
+    if not nm:
+        raise NotImplementedError
+    argv = [nm,
+            '--line-numbers',
+            binary,
+            ]
+    try:
+        output = _run(argv)
+    except Exception:
+        if nm is None:
+            # XXX Use dumpbin.exe /SYMBOLS on Windows.
+            raise NotImplementedError
+        raise
+    for line in output.splitlines():
+        (name, kind, external, filename, funcname, vartype,
+         ) = _parse_nm_line(line,
+                            _find_local_symbol=find_local_symbol,
+                            )
+        if kind != Symbol.KIND.VARIABLE:
+            continue
+        elif _is_special_symbol(name):
+            continue
+        assert vartype is None
+        yield Symbol(
+                id=(filename, funcname, name),
+                kind=kind,
+                external=external,
+                )
+
+
+def _parse_nm_line(line, *, _find_local_symbol=None):
+    _origline = line
+    _, _, line = line.partition(' ')  # strip off the address
+    line = line.strip()
+
+    kind, _, line = line.partition(' ')
+    line = line.strip()
+    external = kind.isupper()
+    kind = NM_KINDS.get(kind.lower(), Symbol.KIND.OTHER)
+
+    name, _, filename = line.partition('\t')
+    name = name.strip()
+    if filename:
+        filename = os.path.relpath(filename.partition(':')[0])
+    else:
+        filename = info.UNKNOWN
+
+    vartype = None
+    name, islocal = _parse_nm_name(name, kind)
+    if islocal:
+        funcname = info.UNKNOWN
+        if _find_local_symbol is not None:
+            filename, funcname, vartype = _find_local_symbol(name)
+            filename = filename or info.UNKNOWN
+            funcname = funcname or info.UNKNOWN
+    else:
+        funcname = None
+        # XXX fine filename and vartype?
+    return name, kind, external, filename, funcname, vartype
+
+
+def _parse_nm_name(name, kind):
+    if kind != Symbol.KIND.VARIABLE:
+        return name, None
+    if _is_special_symbol(name):
+        return name, None
+
+    actual, sep, digits = name.partition('.')
+    if not sep:
+        return name, False
+
+    if not digits.isdigit():
+        raise Exception(f'got bogus name {name}')
+    return actual, True
diff --git a/Tools/c-analyzer/c_symbols/info.py b/Tools/c-analyzer/c_symbols/info.py
new file mode 100644 (file)
index 0000000..f6ed52c
--- /dev/null
@@ -0,0 +1,51 @@
+from collections import namedtuple
+
+from c_analyzer_common.info import ID
+from c_analyzer_common.util import classonly, _NTBase
+
+
+class Symbol(_NTBase, namedtuple('Symbol', 'id kind external')):
+    """Info for a single compilation symbol."""
+
+    __slots__ = ()
+
+    class KIND:
+        VARIABLE = 'variable'
+        FUNCTION = 'function'
+        OTHER = 'other'
+
+    @classonly
+    def from_name(cls, name, filename=None, kind=KIND.VARIABLE, external=None):
+        """Return a new symbol based on the given name."""
+        id = ID(filename, None, name)
+        return cls(id, kind, external)
+
+    def __new__(cls, id, kind=KIND.VARIABLE, external=None):
+        self = super().__new__(
+                cls,
+                id=ID.from_raw(id),
+                kind=str(kind) if kind else None,
+                external=bool(external) if external is not None else None,
+                )
+        return self
+
+    def __hash__(self):
+        return hash(self.id)
+
+    def __getattr__(self, name):
+        return getattr(self.id, name)
+
+    def validate(self):
+        """Fail if the object is invalid (i.e. init with bad data)."""
+        if not self.id:
+            raise TypeError('missing id')
+        else:
+            self.id.validate()
+
+        if not self.kind:
+            raise TypeError('missing kind')
+        elif self.kind not in vars(self.KIND).values():
+            raise ValueError(f'unsupported kind {self.kind}')
+
+        if self.external is None:
+            raise TypeError('missing external')
diff --git a/Tools/c-analyzer/c_symbols/resolve.py b/Tools/c-analyzer/c_symbols/resolve.py
new file mode 100644 (file)
index 0000000..dc876ae
--- /dev/null
@@ -0,0 +1,149 @@
+import os.path
+
+from c_analyzer_common import files
+from c_analyzer_common.info import UNKNOWN
+from c_parser import declarations, info
+from .info import Symbol
+from .source import _find_symbol
+
+
+# XXX need tests:
+# * look_up_known_symbol()
+# * symbol_from_source()
+# * get_resolver()
+# * symbols_to_variables()
+
+def look_up_known_symbol(symbol, knownvars, *,
+                         match_files=(lambda f1, f2: f1 == f2),
+                         ):
+    """Return the known variable matching the given symbol.
+
+    "knownvars" is a mapping of common.ID to parser.Variable.
+
+    "match_files" is used to verify if two filenames point to
+    the same file.
+    """
+    if not knownvars:
+        return None
+
+    if symbol.funcname == UNKNOWN:
+        if not symbol.filename or symbol.filename == UNKNOWN:
+            for varid in knownvars:
+                if not varid.funcname:
+                    continue
+                if varid.name == symbol.name:
+                    return knownvars[varid]
+            else:
+                return None
+        else:
+            for varid in knownvars:
+                if not varid.funcname:
+                    continue
+                if not match_files(varid.filename, symbol.filename):
+                    continue
+                if varid.name == symbol.name:
+                    return knownvars[varid]
+            else:
+                return None
+    elif not symbol.filename or symbol.filename == UNKNOWN:
+        raise NotImplementedError
+    else:
+        return knownvars.get(symbol.id)
+
+
+def find_in_source(symbol, dirnames, *,
+                   _perfilecache={},
+                   _find_symbol=_find_symbol,
+                   _iter_files=files.iter_files_by_suffix,
+                   ):
+    """Return the Variable matching the given Symbol.
+
+    If there is no match then return None.
+    """
+    if symbol.filename and symbol.filename != UNKNOWN:
+        filenames = [symbol.filename]
+    else:
+        filenames = _iter_files(dirnames, ('.c', '.h'))
+
+    if symbol.funcname and symbol.funcname != UNKNOWN:
+        raise NotImplementedError
+
+    (filename, funcname, vartype
+     ) = _find_symbol(symbol.name, filenames, _perfilecache)
+    if filename == UNKNOWN:
+        return None
+    return info.Variable(
+            id=(filename, funcname, symbol.name),
+            vartype=vartype,
+            )
+
+
+def get_resolver(knownvars=None, dirnames=None, *,
+                 _look_up_known=look_up_known_symbol,
+                 _from_source=find_in_source,
+                 ):
+    """Return a "resolver" func for the given known vars and dirnames.
+
+    The func takes a single Symbol and returns a corresponding Variable.
+    If the symbol was located then the variable will be valid, populated
+    with the corresponding information.  Otherwise None is returned.
+    """
+    if knownvars:
+        knownvars = dict(knownvars)  # a copy
+        def resolve_known(symbol):
+            found = _look_up_known(symbol, knownvars)
+            if found is None:
+                return None
+            elif symbol.funcname == UNKNOWN:
+                knownvars.pop(found.id)
+            elif not symbol.filename or symbol.filename == UNKNOWN:
+                knownvars.pop(found.id)
+            return found
+        if dirnames:
+            def resolve(symbol):
+                found = resolve_known(symbol)
+                if found is None:
+                    return None
+                    #return _from_source(symbol, dirnames)
+                else:
+                    for dirname in dirnames:
+                        if not dirname.endswith(os.path.sep):
+                            dirname += os.path.sep
+                        if found.filename.startswith(dirname):
+                            break
+                    else:
+                        return None
+                    return found
+        else:
+            resolve = resolve_known
+    elif dirnames:
+        def resolve(symbol):
+            return _from_source(symbol, dirnames)
+    else:
+        def resolve(symbol):
+            return None
+    return resolve
+
+
+def symbols_to_variables(symbols, *,
+                         resolve=(lambda s: look_up_known_symbol(s, None)),
+                         ):
+    """Yield the variable the matches each given symbol.
+
+    Use get_resolver() for a "resolve" func to use.
+    """
+    for symbol in symbols:
+        if isinstance(symbol, info.Variable):
+            # XXX validate?
+            yield symbol
+            continue
+        if symbol.kind != Symbol.KIND.VARIABLE:
+            continue
+        resolved = resolve(symbol)
+        if resolved is None:
+            #raise NotImplementedError(symbol)
+            resolved = info.Variable(
+                    id=symbol.id,
+                    vartype=UNKNOWN,
+                    )
+        yield resolved
diff --git a/Tools/c-analyzer/c_symbols/source.py b/Tools/c-analyzer/c_symbols/source.py
new file mode 100644 (file)
index 0000000..a724810
--- /dev/null
@@ -0,0 +1,58 @@
+from c_analyzer_common import files
+from c_analyzer_common.info import UNKNOWN
+from c_parser import declarations
+
+
+# XXX need tests:
+# * find_symbol()
+
+def find_symbol(name, dirnames, *,
+                _perfilecache,
+                _iter_files=files.iter_files_by_suffix,
+                **kwargs
+                ):
+    """Return (filename, funcname, vartype) for the matching Symbol."""
+    filenames = _iter_files(dirnames, ('.c', '.h'))
+    return _find_symbol(name, filenames, _perfilecache, **kwargs)
+
+
+def _get_symbols(filename, *,
+                 _iter_variables=declarations.iter_variables,
+                 ):
+    """Return the list of Symbols found in the given file."""
+    symbols = {}
+    for funcname, name, vartype in _iter_variables(filename):
+        if not funcname:
+            continue
+        try:
+            instances = symbols[name]
+        except KeyError:
+            instances = symbols[name] = []
+        instances.append((funcname, vartype))
+    return symbols
+
+
+def _find_symbol(name, filenames, _perfilecache, *,
+                _get_local_symbols=_get_symbols,
+                ):
+    for filename in filenames:
+        try:
+            symbols = _perfilecache[filename]
+        except KeyError:
+            symbols = _perfilecache[filename] = _get_local_symbols(filename)
+
+        try:
+            instances = symbols[name]
+        except KeyError:
+            continue
+
+        funcname, vartype = instances.pop(0)
+        if not instances:
+            symbols.pop(name)
+        return filename, funcname, vartype
+    else:
+        return UNKNOWN, UNKNOWN, UNKNOWN
+
+
+def iter_symbols():
+    raise NotImplementedError
diff --git a/Tools/c-analyzer/ignored.tsv b/Tools/c-analyzer/ignored.tsv
new file mode 100644 (file)
index 0000000..a0e0e50
--- /dev/null
@@ -0,0 +1 @@
+filename       funcname        name    kind    reason
diff --git a/Tools/c-analyzer/known.tsv b/Tools/c-analyzer/known.tsv
new file mode 100644 (file)
index 0000000..ce2afcb
--- /dev/null
@@ -0,0 +1,1922 @@
+filename       funcname        name    kind    declaration
+Modules/_abc.c -       _abc_data_type  variable        static PyTypeObject _abc_data_type
+Modules/_abc.c -       abc_invalidation_counter        variable        static unsigned long long abc_invalidation_counter
+Modules/_abc.c -       _abcmodule      variable        static struct PyModuleDef _abcmodule
+Python/import.c        import_find_and_load    accumulated     variable        static _PyTime_t accumulated
+Modules/itertoolsmodule.c      -       accumulate_methods      variable        static PyMethodDef accumulate_methods
+Modules/itertoolsmodule.c      -       accumulate_type variable        static PyTypeObject accumulate_type
+Python/Python-ast.c    -       Add_singleton   variable        static PyObject *Add_singleton
+Python/Python-ast.c    -       Add_type        variable        static PyTypeObject *Add_type
+Objects/genobject.c    -       ag_asend_freelist       variable        static PyAsyncGenASend *ag_asend_freelist[_PyAsyncGen_MAXFREELIST]
+Objects/genobject.c    -       ag_asend_freelist_free  variable        static int ag_asend_freelist_free
+Objects/genobject.c    -       ag_value_freelist       variable        static _PyAsyncGenWrappedValue *ag_value_freelist[_PyAsyncGen_MAXFREELIST]
+Objects/genobject.c    -       ag_value_freelist_free  variable        static int ag_value_freelist_free
+Python/Python-ast.c    -       alias_fields    variable        static const char *alias_fields[]
+Python/Python-ast.c    -       alias_type      variable        static PyTypeObject *alias_type
+Modules/_tracemalloc.c -       allocators      variable        static struct { PyMemAllocatorEx mem; PyMemAllocatorEx raw; PyMemAllocatorEx obj; } allocators
+Python/Python-ast.c    -       And_singleton   variable        static PyObject *And_singleton
+Python/Python-ast.c    -       And_type        variable        static PyTypeObject *And_type
+Python/Python-ast.c    -       AnnAssign_fields        variable        static const char *AnnAssign_fields[]
+Python/Python-ast.c    -       AnnAssign_type  variable        static PyTypeObject *AnnAssign_type
+Python/compile.c       -       __annotations__ variable        static PyObject *__annotations__
+Objects/obmalloc.c     -       arenas  variable        static struct arena_object* arenas
+Python/Python-ast.c    -       arg_attributes  variable        static const char *arg_attributes[]
+Python/Python-ast.c    -       arg_fields      variable        static const char *arg_fields[]
+Python/Python-ast.c    -       arg_type        variable        static PyTypeObject *arg_type
+Python/Python-ast.c    -       arguments_fields        variable        static const char *arguments_fields[]
+Python/Python-ast.c    -       arguments_type  variable        static PyTypeObject *arguments_type
+Python/Python-ast.c    -       Assert_fields   variable        static const char *Assert_fields[]
+Python/compile.c       compiler_assert assertion_error variable        static PyObject *assertion_error
+Python/Python-ast.c    -       Assert_type     variable        static PyTypeObject *Assert_type
+Python/Python-ast.c    -       Assign_fields   variable        static const char *Assign_fields[]
+Python/Python-ast.c    -       Assign_type     variable        static PyTypeObject *Assign_type
+Python/Python-ast.c    -       _astmodule      variable        static struct PyModuleDef _astmodule
+Python/Python-ast.c    -       AST_type        variable        static PyTypeObject AST_type
+Python/Python-ast.c    -       ast_type_getsets        variable        static PyGetSetDef ast_type_getsets[]
+Python/Python-ast.c    -       ast_type_methods        variable        static PyMethodDef ast_type_methods
+Python/Python-ast.c    -       AsyncFor_fields variable        static const char *AsyncFor_fields[]
+Python/Python-ast.c    -       AsyncFor_type   variable        static PyTypeObject *AsyncFor_type
+Python/Python-ast.c    -       AsyncFunctionDef_fields variable        static const char *AsyncFunctionDef_fields[]
+Python/Python-ast.c    -       AsyncFunctionDef_type   variable        static PyTypeObject *AsyncFunctionDef_type
+Objects/genobject.c    -       async_gen_as_async      variable        static PyAsyncMethods async_gen_as_async
+Objects/genobject.c    -       async_gen_asend_as_async        variable        static PyAsyncMethods async_gen_asend_as_async
+Objects/genobject.c    -       async_gen_asend_methods variable        static PyMethodDef async_gen_asend_methods
+Objects/genobject.c    -       async_gen_athrow_as_async       variable        static PyAsyncMethods async_gen_athrow_as_async
+Objects/genobject.c    -       async_gen_athrow_methods        variable        static PyMethodDef async_gen_athrow_methods
+Objects/genobject.c    -       async_gen_getsetlist    variable        static PyGetSetDef async_gen_getsetlist[]
+Python/sysmodule.c     -       asyncgen_hooks_desc     variable        static PyStructSequence_Desc asyncgen_hooks_desc
+Python/sysmodule.c     -       asyncgen_hooks_fields   variable        static PyStructSequence_Field asyncgen_hooks_fields[]
+Python/sysmodule.c     -       AsyncGenHooksType       variable        static PyTypeObject AsyncGenHooksType
+Objects/genobject.c    -       async_gen_memberlist    variable        static PyMemberDef async_gen_memberlist[]
+Objects/genobject.c    -       async_gen_methods       variable        static PyMethodDef async_gen_methods
+Python/Python-ast.c    -       AsyncWith_fields        variable        static const char *AsyncWith_fields[]
+Python/Python-ast.c    -       AsyncWith_type  variable        static PyTypeObject *AsyncWith_type
+Parser/listnode.c      -       atbol   variable        static int atbol
+Modules/atexitmodule.c -       atexit_methods  variable        static PyMethodDef atexit_methods
+Modules/atexitmodule.c -       atexitmodule    variable        static struct PyModuleDef atexitmodule
+Modules/atexitmodule.c -       atexit_slots    variable        static PyModuleDef_Slot atexit_slots[]
+Modules/_operator.c    -       attrgetter_methods      variable        static PyMethodDef attrgetter_methods
+Modules/_operator.c    -       attrgetter_type variable        static PyTypeObject attrgetter_type
+Python/Python-ast.c    -       Attribute_fields        variable        static const char *Attribute_fields[]
+Python/Python-ast.c    -       Attribute_type  variable        static PyTypeObject *Attribute_type
+Python/Python-ast.c    -       AugAssign_fields        variable        static const char *AugAssign_fields[]
+Python/Python-ast.c    -       AugAssign_type  variable        static PyTypeObject *AugAssign_type
+Python/Python-ast.c    -       AugLoad_singleton       variable        static PyObject *AugLoad_singleton
+Python/Python-ast.c    -       AugLoad_type    variable        static PyTypeObject *AugLoad_type
+Python/Python-ast.c    -       AugStore_singleton      variable        static PyObject *AugStore_singleton
+Python/Python-ast.c    -       AugStore_type   variable        static PyTypeObject *AugStore_type
+Python/Python-ast.c    -       Await_fields    variable        static const char *Await_fields[]
+Python/Python-ast.c    -       Await_type      variable        static PyTypeObject *Await_type
+Objects/exceptions.c   -       BaseException_getset    variable        static PyGetSetDef BaseException_getset[]
+Objects/exceptions.c   -       BaseException_members   variable        static struct PyMemberDef BaseException_members[]
+Objects/exceptions.c   -       BaseException_methods   variable        static PyMethodDef BaseException_methods
+Modules/posixmodule.c  -       billion variable        static PyObject *billion
+Python/Python-ast.c    -       BinOp_fields    variable        static const char *BinOp_fields[]
+Python/Python-ast.c    -       BinOp_type      variable        static PyTypeObject *BinOp_type
+Python/Python-ast.c    -       BitAnd_singleton        variable        static PyObject *BitAnd_singleton
+Python/Python-ast.c    -       BitAnd_type     variable        static PyTypeObject *BitAnd_type
+Python/Python-ast.c    -       BitOr_singleton variable        static PyObject *BitOr_singleton
+Python/Python-ast.c    -       BitOr_type      variable        static PyTypeObject *BitOr_type
+Python/Python-ast.c    -       BitXor_singleton        variable        static PyObject *BitXor_singleton
+Python/Python-ast.c    -       BitXor_type     variable        static PyTypeObject *BitXor_type
+Objects/unicodeobject.c        -       bloom_linebreak variable        static BLOOM_MASK bloom_linebreak
+Objects/boolobject.c   -       bool_as_number  variable        static PyNumberMethods bool_as_number
+Python/Python-ast.c    -       BoolOp_fields   variable        static const char *BoolOp_fields[]
+Python/Python-ast.c    -       boolop_type     variable        static PyTypeObject *boolop_type
+Python/Python-ast.c    -       BoolOp_type     variable        static PyTypeObject *BoolOp_type
+Python/_warnings.c     is_internal_frame       bootstrap_string        variable        static PyObject *bootstrap_string
+Python/Python-ast.c    -       Break_type      variable        static PyTypeObject *Break_type
+Modules/_io/bufferedio.c       -       bufferediobase_methods  variable        static PyMethodDef bufferediobase_methods
+Modules/_io/bufferedio.c       -       bufferedrandom_getset   variable        static PyGetSetDef bufferedrandom_getset[]
+Modules/_io/bufferedio.c       -       bufferedrandom_members  variable        static PyMemberDef bufferedrandom_members[]
+Modules/_io/bufferedio.c       -       bufferedrandom_methods  variable        static PyMethodDef bufferedrandom_methods
+Modules/_io/bufferedio.c       -       bufferedreader_getset   variable        static PyGetSetDef bufferedreader_getset[]
+Modules/_io/bufferedio.c       -       bufferedreader_members  variable        static PyMemberDef bufferedreader_members[]
+Modules/_io/bufferedio.c       -       bufferedreader_methods  variable        static PyMethodDef bufferedreader_methods
+Modules/_io/bufferedio.c       -       bufferedrwpair_getset   variable        static PyGetSetDef bufferedrwpair_getset[]
+Modules/_io/bufferedio.c       -       bufferedrwpair_methods  variable        static PyMethodDef bufferedrwpair_methods
+Modules/_io/bufferedio.c       -       bufferedwriter_getset   variable        static PyGetSetDef bufferedwriter_getset[]
+Modules/_io/bufferedio.c       -       bufferedwriter_members  variable        static PyMemberDef bufferedwriter_members[]
+Modules/_io/bufferedio.c       -       bufferedwriter_methods  variable        static PyMethodDef bufferedwriter_methods
+Modules/getbuildinfo.c Py_GetBuildInfo buildinfo       variable        static char buildinfo[50 + sizeof(GITVERSION) + ((sizeof(GITTAG) > sizeof(GITBRANCH)) ?  sizeof(GITTAG) : sizeof(GITBRANCH))]
+Python/bltinmodule.c   -       builtin_methods variable        static PyMethodDef builtin_methods
+Python/bltinmodule.c   -       builtinsmodule  variable        static struct PyModuleDef builtinsmodule
+Python/import.c        PyImport_Import builtins_str    variable        static PyObject *builtins_str
+Python/ceval.c make_pending_calls      busy    variable        static int busy
+Objects/bytearrayobject.c      -       bytearray_as_buffer     variable        static PyBufferProcs bytearray_as_buffer
+Objects/bytearrayobject.c      -       bytearray_as_mapping    variable        static PyMappingMethods bytearray_as_mapping
+Objects/bytearrayobject.c      -       bytearray_as_number     variable        static PyNumberMethods bytearray_as_number
+Objects/bytearrayobject.c      -       bytearray_as_sequence   variable        static PySequenceMethods bytearray_as_sequence
+Objects/bytearrayobject.c      -       bytearrayiter_methods   variable        static PyMethodDef bytearrayiter_methods
+Objects/bytearrayobject.c      -       bytearray_methods       variable        static PyMethodDef bytearray_methods
+Objects/bytesobject.c  -       bytes_as_buffer variable        static PyBufferProcs bytes_as_buffer
+Objects/bytesobject.c  -       bytes_as_mapping        variable        static PyMappingMethods bytes_as_mapping
+Objects/bytesobject.c  -       bytes_as_number variable        static PyNumberMethods bytes_as_number
+Objects/bytesobject.c  -       bytes_as_sequence       variable        static PySequenceMethods bytes_as_sequence
+Modules/_io/bytesio.c  -       bytesiobuf_as_buffer    variable        static PyBufferProcs bytesiobuf_as_buffer
+Modules/_io/bytesio.c  -       bytesio_getsetlist      variable        static PyGetSetDef bytesio_getsetlist[]
+Modules/_io/bytesio.c  -       bytesio_methods variable        static PyMethodDef bytesio_methods
+Objects/bytesobject.c  -       bytes_methods   variable        static PyMethodDef bytes_methods
+Python/thread_pthread.h        init_condattr   ca      variable        static pthread_condattr_t ca
+Python/Python-ast.c    -       Call_fields     variable        static const char *Call_fields[]
+Objects/iterobject.c   -       calliter_methods        variable        static PyMethodDef calliter_methods
+Python/Python-ast.c    -       Call_type       variable        static PyTypeObject *Call_type
+Objects/cellobject.c   -       cell_getsetlist variable        static PyGetSetDef cell_getsetlist[]
+Modules/itertoolsmodule.c      -       chain_methods   variable        static PyMethodDef chain_methods
+Modules/itertoolsmodule.c      -       chain_type      variable        static PyTypeObject chain_type
+Objects/bytesobject.c  -       characters      variable        static PyBytesObject *characters[UCHAR_MAX + 1]
+Python/symtable.c      -       __class__       variable        static identifier __class__
+Python/Python-ast.c    -       ClassDef_fields variable        static const char *ClassDef_fields[]
+Python/Python-ast.c    -       ClassDef_type   variable        static PyTypeObject *ClassDef_type
+Objects/funcobject.c   -       cm_getsetlist   variable        static PyGetSetDef cm_getsetlist[]
+Objects/funcobject.c   -       cm_memberlist   variable        static PyMemberDef cm_memberlist[]
+Python/Python-ast.c    -       cmpop_type      variable        static PyTypeObject *cmpop_type
+Modules/_codecsmodule.c        -       _codecs_functions       variable        static PyMethodDef _codecs_functions[]
+Modules/_codecsmodule.c        -       codecsmodule    variable        static struct PyModuleDef codecsmodule
+Objects/codeobject.c   -       code_memberlist variable        static PyMemberDef code_memberlist[]
+Objects/codeobject.c   -       code_methods    variable        static PyMethodDef code_methods
+Modules/_collectionsmodule.c   -       _collectionsmodule      variable        static struct PyModuleDef _collectionsmodule
+Modules/itertoolsmodule.c      -       combinations_methods    variable        static PyMethodDef combinations_methods
+Modules/itertoolsmodule.c      -       combinations_type       variable        static PyTypeObject combinations_type
+Objects/typeobject.c   object_new      comma_id        variable        _Py_static_string(comma_id, "", "")
+Python/Python-ast.c    -       Compare_fields  variable        static const char *Compare_fields[]
+Python/Python-ast.c    -       Compare_type    variable        static PyTypeObject *Compare_type
+Objects/complexobject.c        -       complex_as_number       variable        static PyNumberMethods complex_as_number
+Objects/complexobject.c        -       complex_members variable        static PyMemberDef complex_members[]
+Objects/complexobject.c        -       complex_methods variable        static PyMethodDef complex_methods
+Python/Python-ast.c    -       comprehension_fields    variable        static const char *comprehension_fields[]
+Python/Python-ast.c    -       comprehension_type      variable        static PyTypeObject *comprehension_type
+Modules/itertoolsmodule.c      -       compress_methods        variable        static PyMethodDef compress_methods
+Modules/itertoolsmodule.c      -       compress_type   variable        static PyTypeObject compress_type
+Python/thread_pthread.h        -       condattr_monotonic      variable        static pthread_condattr_t *condattr_monotonic
+Python/Python-ast.c    -       Constant_fields variable        static const char *Constant_fields[]
+Python/Python-ast.c    -       Constant_type   variable        static PyTypeObject *Constant_type
+Python/Python-ast.c    -       Continue_type   variable        static PyTypeObject *Continue_type
+Objects/longobject.c   PyLong_FromString       convmultmax_base        variable        static twodigits convmultmax_base[37]
+Objects/longobject.c   PyLong_FromString       convwidth_base  variable        static int convwidth_base[37]
+Objects/genobject.c    -       coro_as_async   variable        static PyAsyncMethods coro_as_async
+Objects/genobject.c    -       coro_getsetlist variable        static PyGetSetDef coro_getsetlist[]
+Objects/genobject.c    -       coro_memberlist variable        static PyMemberDef coro_memberlist[]
+Objects/genobject.c    -       coro_methods    variable        static PyMethodDef coro_methods
+Objects/genobject.c    -       coro_wrapper_methods    variable        static PyMethodDef coro_wrapper_methods
+Modules/itertoolsmodule.c      -       count_methods   variable        static PyMethodDef count_methods
+Modules/itertoolsmodule.c      -       count_type      variable        static PyTypeObject count_type
+Python/context.c       -       ctx_freelist    variable        static PyContext *ctx_freelist
+Python/context.c       -       ctx_freelist_len        variable        static int ctx_freelist_len
+Modules/itertoolsmodule.c      -       cwr_methods     variable        static PyMethodDef cwr_methods
+Modules/itertoolsmodule.c      -       cwr_type        variable        static PyTypeObject cwr_type
+Modules/itertoolsmodule.c      -       cycle_methods   variable        static PyMethodDef cycle_methods
+Modules/itertoolsmodule.c      -       cycle_type      variable        static PyTypeObject cycle_type
+Objects/obmalloc.c     new_arena       debug_stats     variable        static int debug_stats
+Modules/signalmodule.c -       DefaultHandler  variable        static PyObject *DefaultHandler
+Modules/_collectionsmodule.c   -       defdict_members variable        static PyMemberDef defdict_members[]
+Modules/_collectionsmodule.c   -       defdict_methods variable        static PyMethodDef defdict_methods
+Modules/_collectionsmodule.c   -       defdict_type    variable        static PyTypeObject defdict_type
+Python/Python-ast.c    -       Delete_fields   variable        static const char *Delete_fields[]
+Python/Python-ast.c    -       Delete_type     variable        static PyTypeObject *Delete_type
+Python/Python-ast.c    -       Del_singleton   variable        static PyObject *Del_singleton
+Python/Python-ast.c    -       Del_type        variable        static PyTypeObject *Del_type
+Modules/_collectionsmodule.c   -       deque_as_number variable        static PyNumberMethods deque_as_number
+Modules/_collectionsmodule.c   -       deque_as_sequence       variable        static PySequenceMethods deque_as_sequence
+Modules/_collectionsmodule.c   -       deque_getset    variable        static PyGetSetDef deque_getset[]
+Modules/_collectionsmodule.c   -       dequeiter_methods       variable        static PyMethodDef dequeiter_methods
+Modules/_collectionsmodule.c   -       dequeiter_type  variable        static PyTypeObject dequeiter_type
+Modules/_collectionsmodule.c   -       deque_methods   variable        static PyMethodDef deque_methods
+Modules/_collectionsmodule.c   -       dequereviter_type       variable        static PyTypeObject dequereviter_type
+Modules/_collectionsmodule.c   -       deque_type      variable        static PyTypeObject deque_type
+Objects/descrobject.c  -       descr_members   variable        static PyMemberDef descr_members[]
+Objects/descrobject.c  -       descr_methods   variable        static PyMethodDef descr_methods
+Modules/_abc.c -       _destroy_def    variable        static PyMethodDef _destroy_def
+Objects/floatobject.c  -       detected_double_format  variable        static float_format_type detected_double_format
+Objects/floatobject.c  -       detected_float_format   variable        static float_format_type detected_float_format
+Objects/dictobject.c   -       dict_as_mapping variable        static PyMappingMethods dict_as_mapping
+Objects/dictobject.c   -       dict_as_sequence        variable        static PySequenceMethods dict_as_sequence
+Python/symtable.c      -       dictcomp        variable        static identifier dictcomp
+Python/Python-ast.c    -       DictComp_fields variable        static const char *DictComp_fields[]
+Python/Python-ast.c    -       DictComp_type   variable        static PyTypeObject *DictComp_type
+Python/Python-ast.c    -       Dict_fields     variable        static const char *Dict_fields[]
+Objects/dictobject.c   -       dictitems_as_sequence   variable        static PySequenceMethods dictitems_as_sequence
+Objects/dictobject.c   -       dictitems_methods       variable        static PyMethodDef dictitems_methods
+Objects/dictobject.c   -       dictiter_methods        variable        static PyMethodDef dictiter_methods
+Objects/dictobject.c   -       dictkeys_as_sequence    variable        static PySequenceMethods dictkeys_as_sequence
+Objects/dictobject.c   -       dictkeys_methods        variable        static PyMethodDef dictkeys_methods
+Python/Python-ast.c    -       Dict_type       variable        static PyTypeObject *Dict_type
+Objects/dictobject.c   -       dictvalues_as_sequence  variable        static PySequenceMethods dictvalues_as_sequence
+Objects/dictobject.c   -       dictvalues_methods      variable        static PyMethodDef dictvalues_methods
+Objects/dictobject.c   -       dictviews_as_number     variable        static PyNumberMethods dictviews_as_number
+Modules/posixmodule.c  -       DirEntry_members        variable        static PyMemberDef DirEntry_members[]
+Modules/posixmodule.c  -       DirEntry_methods        variable        static PyMethodDef DirEntry_methods
+Modules/posixmodule.c  -       DirEntryType    variable        static PyTypeObject DirEntryType
+Python/Python-ast.c    -       Div_singleton   variable        static PyObject *Div_singleton
+Python/Python-ast.c    -       Div_type        variable        static PyTypeObject *Div_type
+Python/compile.c       -       __doc__ variable        static PyObject *__doc__
+Objects/classobject.c  method_get_doc  docstr  variable        static PyObject *docstr
+Objects/classobject.c  instancemethod_get_doc  docstr  variable        static PyObject *docstr
+Python/compile.c       compiler_set_qualname   dot     variable        _Py_static_string(dot, ""."")
+Python/compile.c       compiler_set_qualname   dot_locals      variable        _Py_static_string(dot_locals, "".<locals>"")
+Objects/floatobject.c  -       double_format   variable        static float_format_type double_format
+Modules/itertoolsmodule.c      -       dropwhile_methods       variable        static PyMethodDef dropwhile_methods
+Modules/itertoolsmodule.c      -       dropwhile_type  variable        static PyTypeObject dropwhile_type
+Objects/setobject.c    -       _dummy_struct   variable        static PyObject _dummy_struct
+Modules/posixmodule.c  os_dup2_impl    dup3_works      variable        static int dup3_works
+Modules/_io/bufferedio.c       _PyIO_trap_eintr        eintr_int       variable        static PyObject *eintr_int
+Objects/sliceobject.c  -       ellipsis_methods        variable        static PyMethodDef ellipsis_methods
+Python/hamt.c  -       _empty_bitmap_node      variable        static PyHamtNode_Bitmap *_empty_bitmap_node
+Objects/setobject.c    -       emptyfrozenset  variable        static PyObject *emptyfrozenset
+Python/hamt.c  -       _empty_hamt     variable        static PyHamtObject *_empty_hamt
+Objects/dictobject.c   -       empty_keys_struct       variable        static PyDictKeysObject empty_keys_struct
+Objects/codeobject.c   PyCode_NewEmpty emptystring     variable        static PyObject *emptystring
+Python/compile.c       compiler_from_import    empty_string    variable        static PyObject *empty_string
+Objects/dictobject.c   -       empty_values    variable        static PyObject *empty_values[1]
+Objects/unicodeobject.c        -       encoding_map_methods    variable        static PyMethodDef encoding_map_methods
+Objects/unicodeobject.c        -       EncodingMapType variable        static PyTypeObject EncodingMapType
+Objects/enumobject.c   -       enum_methods    variable        static PyMethodDef enum_methods
+Python/Python-ast.c    -       Eq_singleton    variable        static PyObject *Eq_singleton
+Python/Python-ast.c    -       Eq_type variable        static PyTypeObject *Eq_type
+Objects/exceptions.c   -       errnomap        variable        static PyObject *errnomap
+Modules/errnomodule.c  -       errno_methods   variable        static PyMethodDef errno_methods
+Modules/errnomodule.c  -       errnomodule     variable        static struct PyModuleDef errnomodule
+Modules/_localemodule.c        -       Error   variable        static PyObject *Error
+Python/Python-ast.c    -       excepthandler_attributes        variable        static const char *excepthandler_attributes[]
+Python/Python-ast.c    -       ExceptHandler_fields    variable        static const char *ExceptHandler_fields[]
+Python/Python-ast.c    -       excepthandler_type      variable        static PyTypeObject *excepthandler_type
+Python/Python-ast.c    -       ExceptHandler_type      variable        static PyTypeObject *ExceptHandler_type
+Modules/_threadmodule.c        -       ExceptHookArgs_desc     variable        static PyStructSequence_Desc ExceptHookArgs_desc
+Modules/_threadmodule.c        -       ExceptHookArgs_fields   variable        static PyStructSequence_Field ExceptHookArgs_fields[]
+Modules/_threadmodule.c        -       ExceptHookArgsType      variable        static PyTypeObject ExceptHookArgsType
+Objects/exceptions.c   _check_for_legacy_statements    exec_prefix     variable        static PyObject *exec_prefix
+Python/Python-ast.c    -       expr_attributes variable        static const char *expr_attributes[]
+Python/Python-ast.c    -       expr_context_type       variable        static PyTypeObject *expr_context_type
+Python/Python-ast.c    -       Expression_fields       variable        static const char *Expression_fields[]
+Python/Python-ast.c    -       Expression_type variable        static PyTypeObject *Expression_type
+Python/Python-ast.c    -       Expr_fields     variable        static const char *Expr_fields[]
+Python/Python-ast.c    -       expr_type       variable        static PyTypeObject *expr_type
+Python/Python-ast.c    -       Expr_type       variable        static PyTypeObject *Expr_type
+Python/import.c        -       extensions      variable        static PyObject *extensions
+Python/Python-ast.c    -       ExtSlice_fields variable        static const char *ExtSlice_fields[]
+Python/Python-ast.c    -       ExtSlice_type   variable        static PyTypeObject *ExtSlice_type
+Objects/boolobject.c   -       false_str       variable        static PyObject *false_str
+Modules/faulthandler.c -       fatal_error     variable        static struct { int enabled; PyObject *file; int fd; int all_threads; PyInterpreterState *interp; void *exc_handler; } fatal_error
+Modules/faulthandler.c -       faulthandler_handlers   variable        static fault_handler_t faulthandler_handlers[]
+Objects/stringlib/unicode_format.h     -       fieldnameiter_methods   variable        static PyMethodDef fieldnameiter_methods
+Modules/_io/fileio.c   -       fileio_getsetlist       variable        static PyGetSetDef fileio_getsetlist[]
+Modules/_io/fileio.c   -       fileio_members  variable        static PyMemberDef fileio_members[]
+Modules/_io/fileio.c   -       fileio_methods  variable        static PyMethodDef fileio_methods
+Modules/itertoolsmodule.c      -       filterfalse_methods     variable        static PyMethodDef filterfalse_methods
+Modules/itertoolsmodule.c      -       filterfalse_type        variable        static PyTypeObject filterfalse_type
+Python/bltinmodule.c   -       filter_methods  variable        static PyMethodDef filter_methods
+Python/sysmodule.c     -       flags_desc      variable        static PyStructSequence_Desc flags_desc
+Python/sysmodule.c     -       flags_fields    variable        static PyStructSequence_Field flags_fields[]
+Python/sysmodule.c     -       FlagsType       variable        static PyTypeObject FlagsType
+Objects/floatobject.c  -       float_as_number variable        static PyNumberMethods float_as_number
+Objects/floatobject.c  -       float_format    variable        static float_format_type 
+Objects/floatobject.c  -       float_getset    variable        static PyGetSetDef float_getset[]
+Objects/floatobject.c  -       floatinfo_desc  variable        static PyStructSequence_Desc floatinfo_desc
+Objects/floatobject.c  -       floatinfo_fields        variable        static PyStructSequence_Field floatinfo_fields[]
+Objects/floatobject.c  -       FloatInfoType   variable        static PyTypeObject FloatInfoType
+Objects/floatobject.c  -       float_methods   variable        static PyMethodDef float_methods
+Python/Python-ast.c    -       FloorDiv_singleton      variable        static PyObject *FloorDiv_singleton
+Python/Python-ast.c    -       FloorDiv_type   variable        static PyTypeObject *FloorDiv_type
+Python/fileutils.c     -       force_ascii     variable        static int force_ascii
+Python/Python-ast.c    -       For_fields      variable        static const char *For_fields[]
+Python/Python-ast.c    -       FormattedValue_fields   variable        static const char *FormattedValue_fields[]
+Python/Python-ast.c    -       FormattedValue_type     variable        static PyTypeObject *FormattedValue_type
+Objects/stringlib/unicode_format.h     -       formatteriter_methods   variable        static PyMethodDef formatteriter_methods
+Python/Python-ast.c    -       For_type        variable        static PyTypeObject *For_type
+Objects/frameobject.c  -       frame_getsetlist        variable        static PyGetSetDef frame_getsetlist[]
+Objects/frameobject.c  -       frame_memberlist        variable        static PyMemberDef frame_memberlist[]
+Objects/frameobject.c  -       frame_methods   variable        static PyMethodDef frame_methods
+Modules/_collectionsmodule.c   -       freeblocks      variable        static block *freeblocks[MAXFREEBLOCKS]
+Python/dtoa.c  -       freelist        variable        static Bigint *freelist[Kmax+1]
+Objects/floatobject.c  -       free_list       variable        static PyFloatObject *free_list
+Objects/frameobject.c  -       free_list       variable        static PyFrameObject *free_list
+Objects/listobject.c   -       free_list       variable        static PyListObject *free_list[PyList_MAXFREELIST]
+Objects/dictobject.c   -       free_list       variable        static PyDictObject *free_list[PyDict_MAXFREELIST]
+Objects/methodobject.c -       free_list       variable        static PyCFunctionObject *free_list
+Objects/tupleobject.c  -       free_list       variable        static PyTupleObject *free_list[PyTuple_MAXSAVESIZE]
+Objects/classobject.c  -       free_list       variable        static PyMethodObject *free_list
+Objects/setobject.c    -       frozenset_as_number     variable        static PyNumberMethods frozenset_as_number
+Objects/setobject.c    -       frozenset_methods       variable        static PyMethodDef frozenset_methods
+Objects/funcobject.c   -       func_getsetlist variable        static PyGetSetDef func_getsetlist[]
+Objects/funcobject.c   -       func_memberlist variable        static PyMemberDef func_memberlist[]
+Python/Python-ast.c    -       FunctionDef_fields      variable        static const char *FunctionDef_fields[]
+Python/Python-ast.c    -       FunctionDef_type        variable        static PyTypeObject *FunctionDef_type
+Modules/_sre.c -       _functions      variable        static PyMethodDef _functions[]
+Python/Python-ast.c    -       FunctionType_fields     variable        static const char *FunctionType_fields[]
+Python/Python-ast.c    -       FunctionType_type       variable        static PyTypeObject *FunctionType_type
+Modules/_functoolsmodule.c     -       _functoolsmodule        variable        static struct PyModuleDef _functoolsmodule
+Modules/gcmodule.c     -       GcMethods       variable        static PyMethodDef GcMethods[]
+Modules/gcmodule.c     -       gcmodule        variable        static struct PyModuleDef gcmodule
+Modules/gcmodule.c     -       gc_str  variable        static PyObject *gc_str
+Python/Python-ast.c    -       GeneratorExp_fields     variable        static const char *GeneratorExp_fields[]
+Python/Python-ast.c    -       GeneratorExp_type       variable        static PyTypeObject *GeneratorExp_type
+Python/symtable.c      -       genexpr variable        static identifier genexpr
+Objects/genobject.c    -       gen_getsetlist  variable        static PyGetSetDef gen_getsetlist[]
+Objects/genobject.c    -       gen_memberlist  variable        static PyMemberDef gen_memberlist[]
+Objects/genobject.c    -       gen_methods     variable        static PyMethodDef gen_methods
+Python/bootstrap_hash.c        py_getrandom    getrandom_works variable        static int getrandom_works
+Objects/descrobject.c  -       getset_getset   variable        static PyGetSetDef getset_getset[]
+Python/Python-ast.c    -       Global_fields   variable        static const char *Global_fields[]
+Python/Python-ast.c    -       Global_type     variable        static PyTypeObject *Global_type
+Modules/itertoolsmodule.c      -       groupby_methods variable        static PyMethodDef groupby_methods
+Modules/itertoolsmodule.c      -       groupby_type    variable        static PyTypeObject groupby_type
+Modules/itertoolsmodule.c      -       _grouper_methods        variable        static PyMethodDef _grouper_methods
+Modules/itertoolsmodule.c      -       _grouper_type   variable        static PyTypeObject _grouper_type
+Python/Python-ast.c    -       GtE_singleton   variable        static PyObject *GtE_singleton
+Python/Python-ast.c    -       GtE_type        variable        static PyTypeObject *GtE_type
+Python/Python-ast.c    -       Gt_singleton    variable        static PyObject *Gt_singleton
+Python/Python-ast.c    -       Gt_type variable        static PyTypeObject *Gt_type
+Modules/signalmodule.c -       Handlers        variable        static volatile struct { _Py_atomic_int tripped; PyObject *func; } Handlers[NSIG]
+Python/dynload_shlib.c -       handles variable        static struct { dev_t dev; ino_t ino; void *handle; } handles[128]
+Python/sysmodule.c     -       hash_info_desc  variable        static PyStructSequence_Desc hash_info_desc
+Python/sysmodule.c     -       hash_info_fields        variable        static PyStructSequence_Field hash_info_fields[]
+Python/sysmodule.c     -       Hash_InfoType   variable        static PyTypeObject Hash_InfoType
+Python/import.c        import_find_and_load    header  variable        static int header
+Python/Python-ast.c    -       IfExp_fields    variable        static const char *IfExp_fields[]
+Python/Python-ast.c    -       IfExp_type      variable        static PyTypeObject *IfExp_type
+Python/Python-ast.c    -       If_fields       variable        static const char *If_fields[]
+Python/Python-ast.c    -       If_type variable        static PyTypeObject *If_type
+Modules/signalmodule.c -       IgnoreHandler   variable        static PyObject *IgnoreHandler
+Python/import.c        -       imp_methods     variable        static PyMethodDef imp_methods
+Python/import.c        -       impmodule       variable        static struct PyModuleDef impmodule
+Objects/exceptions.c   -       ImportError_members     variable        static PyMemberDef ImportError_members[]
+Objects/exceptions.c   -       ImportError_methods     variable        static PyMethodDef ImportError_methods
+Python/Python-ast.c    -       Import_fields   variable        static const char *Import_fields[]
+Python/Python-ast.c    -       ImportFrom_fields       variable        static const char *ImportFrom_fields[]
+Python/Python-ast.c    -       ImportFrom_type variable        static PyTypeObject *ImportFrom_type
+Python/import.c        import_find_and_load    import_level    variable        static int import_level
+Python/_warnings.c     is_internal_frame       importlib_string        variable        static PyObject *importlib_string
+Python/import.c        -       import_lock     variable        static PyThread_type_lock import_lock
+Python/import.c        -       import_lock_level       variable        static int import_lock_level
+Python/import.c        -       import_lock_thread      variable        static unsigned long import_lock_thread
+Python/import.c        PyImport_Import import_str      variable        static PyObject *import_str
+Python/Python-ast.c    -       Import_type     variable        static PyTypeObject *Import_type
+Modules/_io/textio.c   -       incrementalnewlinedecoder_getset        variable        static PyGetSetDef incrementalnewlinedecoder_getset[]
+Modules/_io/textio.c   -       incrementalnewlinedecoder_methods       variable        static PyMethodDef incrementalnewlinedecoder_methods
+Objects/listobject.c   -       indexerr        variable        static PyObject *indexerr
+Python/Python-ast.c    -       Index_fields    variable        static const char *Index_fields[]
+Python/Python-ast.c    -       Index_type      variable        static PyTypeObject *Index_type
+Python/thread.c        -       initialized     variable        static int initialized
+Modules/posixmodule.c  -       initialized     variable        static int initialized
+Modules/pwdmodule.c    -       initialized     variable        static int initialized
+Modules/signalmodule.c -       initialized     variable        static int initialized
+Modules/timemodule.c   -       initialized     variable        static int initialized
+Python/Python-ast.c    init_types      initialized     variable        static int initialized
+Objects/listobject.c   PyList_New      initialized     variable        static int initialized
+Python/import.c        -       inittab_copy    variable        static struct _inittab *inittab_copy
+Python/Python-ast.c    -       In_singleton    variable        static PyObject *In_singleton
+Objects/classobject.c  -       instancemethod_getset   variable        static PyGetSetDef instancemethod_getset[]
+Objects/classobject.c  -       instancemethod_memberlist       variable        static PyMemberDef instancemethod_memberlist[]
+Python/Python-ast.c    -       Interactive_fields      variable        static const char *Interactive_fields[]
+Python/Python-ast.c    -       Interactive_type        variable        static PyTypeObject *Interactive_type
+Objects/unicodeobject.c        -       interned        variable        static PyObject *interned
+Objects/interpreteridobject.c  -       interpid_as_number      variable        static PyNumberMethods interpid_as_number
+Modules/signalmodule.c -       IntHandler      variable        static PyObject *IntHandler
+Objects/longobject.c   -       int_info_desc   variable        static PyStructSequence_Desc int_info_desc
+Objects/longobject.c   -       int_info_fields variable        static PyStructSequence_Field int_info_fields[]
+Objects/longobject.c   -       Int_InfoType    variable        static PyTypeObject Int_InfoType
+Python/Python-ast.c    -       In_type variable        static PyTypeObject *In_type
+Python/Python-ast.c    -       Invert_singleton        variable        static PyObject *Invert_singleton
+Python/Python-ast.c    -       Invert_type     variable        static PyTypeObject *Invert_type
+Modules/_io/iobase.c   -       iobase_getset   variable        static PyGetSetDef iobase_getset[]
+Modules/_io/iobase.c   -       iobase_methods  variable        static PyMethodDef iobase_methods
+Python/fileutils.c     set_inheritable ioctl_works     variable        static int ioctl_works
+Modules/itertoolsmodule.c      -       islice_methods  variable        static PyMethodDef islice_methods
+Modules/itertoolsmodule.c      -       islice_type     variable        static PyTypeObject islice_type
+Python/Python-ast.c    -       IsNot_singleton variable        static PyObject *IsNot_singleton
+Python/Python-ast.c    -       IsNot_type      variable        static PyTypeObject *IsNot_type
+Python/Python-ast.c    -       Is_singleton    variable        static PyObject *Is_singleton
+Modules/signalmodule.c -       is_tripped      variable        static _Py_atomic_int is_tripped
+Python/Python-ast.c    -       Is_type variable        static PyTypeObject *Is_type
+Modules/_operator.c    -       itemgetter_methods      variable        static PyMethodDef itemgetter_methods
+Modules/_operator.c    -       itemgetter_type variable        static PyTypeObject itemgetter_type
+Modules/itertoolsmodule.c      -       itertoolsmodule variable        static struct PyModuleDef itertoolsmodule
+Modules/signalmodule.c -       ItimerError     variable        static PyObject *ItimerError
+Python/Python-ast.c    -       JoinedStr_fields        variable        static const char *JoinedStr_fields[]
+Python/Python-ast.c    -       JoinedStr_type  variable        static PyTypeObject *JoinedStr_type
+Modules/_functoolsmodule.c     -       keyobject_members       variable        static PyMemberDef keyobject_members[]
+Modules/_functoolsmodule.c     -       keyobject_type  variable        static PyTypeObject keyobject_type
+Objects/dictobject.c   -       keys_free_list  variable        static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]
+Python/Python-ast.c    -       keyword_fields  variable        static const char *keyword_fields[]
+Python/sysmodule.c     sys_set_asyncgen_hooks  keywords        variable        static const char *keywords[]
+Modules/_bisectmodule.c        bisect_right    keywords        variable        static const char *keywords[]
+Modules/_bisectmodule.c        insort_right    keywords        variable        static const char *keywords[]
+Python/Python-ast.c    -       keyword_type    variable        static PyTypeObject *keyword_type
+Modules/_functoolsmodule.c     keyobject_call  kwargs  variable        static const char *kwargs[]
+Modules/_functoolsmodule.c     functools_cmp_to_key    kwargs  variable        static const char *kwargs[]
+Modules/itertoolsmodule.c      repeat_new      kwargs  variable        static const char *kwargs[]
+Python/_warnings.c     warnings_warn_explicit  kwd_list        variable        static const char *kwd_list[]
+Modules/_functoolsmodule.c     -       kwd_mark        variable        static PyObject *kwd_mark
+Python/bltinmodule.c   builtin___import__      kwlist  variable        static const char *kwlist[]
+Python/bltinmodule.c   min_max kwlist  variable        static const char *kwlist[]
+Python/context.c       contextvar_tp_new       kwlist  variable        static const char *kwlist[]
+Python/sysmodule.c     sys_getsizeof   kwlist  variable        static const char *kwlist[]
+Objects/bytearrayobject.c      bytearray_init  kwlist  variable        static const char *kwlist[]
+Objects/bytesobject.c  bytes_new       kwlist  variable        static const char *kwlist[]
+Objects/exceptions.c   ImportError_init        kwlist  variable        static const char *kwlist[]
+Objects/interpreteridobject.c  interpid_new    kwlist  variable        static const char *kwlist[]
+Objects/memoryobject.c memory_new      kwlist  variable        static const char *kwlist[]
+Objects/memoryobject.c memory_cast     kwlist  variable        static const char *kwlist[]
+Objects/memoryobject.c memory_tobytes  kwlist  variable        static const char *kwlist[]
+Objects/odictobject.c  odict_pop       kwlist  variable        static const char *kwlist[]
+Objects/unicodeobject.c        unicode_new     kwlist  variable        static const char *kwlist[]
+Objects/weakrefobject.c        weakref_call    kwlist  variable        static const char *kwlist[]
+Modules/_elementtree.c element_setstate_from_Python    kwlist  variable        static const char *kwlist[]
+Modules/_json.c        scanner_call    kwlist  variable        static const char *kwlist[]
+Modules/_json.c        scanner_new     kwlist  variable        static const char *kwlist[]
+Modules/_json.c        encoder_new     kwlist  variable        static const char *kwlist[]
+Modules/_json.c        encoder_call    kwlist  variable        static const char *kwlist[]
+Python/symtable.c      -       lambda  variable        static identifier lambda
+Python/Python-ast.c    -       Lambda_fields   variable        static const char *Lambda_fields[]
+Python/Python-ast.c    -       Lambda_type     variable        static PyTypeObject *Lambda_type
+Parser/listnode.c      -       level   variable        static int level
+Objects/listobject.c   -       list_as_mapping variable        static PyMappingMethods list_as_mapping
+Objects/listobject.c   -       list_as_sequence        variable        static PySequenceMethods list_as_sequence
+Python/symtable.c      -       listcomp        variable        static identifier listcomp
+Python/Python-ast.c    -       ListComp_fields variable        static const char *ListComp_fields[]
+Python/Python-ast.c    -       ListComp_type   variable        static PyTypeObject *ListComp_type
+Python/Python-ast.c    -       List_fields     variable        static const char *List_fields[]
+Objects/listobject.c   -       listiter_methods        variable        static PyMethodDef listiter_methods
+Objects/listobject.c   -       list_methods    variable        static PyMethodDef list_methods
+Objects/listobject.c   -       listreviter_methods     variable        static PyMethodDef listreviter_methods
+Python/Python-ast.c    -       List_type       variable        static PyTypeObject *List_type
+Python/ceval.c -       lltrace variable        static int lltrace
+Python/Python-ast.c    -       Load_singleton  variable        static PyObject *Load_singleton
+Python/Python-ast.c    -       Load_type       variable        static PyTypeObject *Load_type
+Modules/_threadmodule.c        -       localdummytype  variable        static PyTypeObject localdummytype
+Modules/_localemodule.c        -       _localemodule   variable        static struct PyModuleDef _localemodule
+Modules/_threadmodule.c        -       localtype       variable        static PyTypeObject localtype
+Modules/_threadmodule.c        -       lock_methods    variable        static PyMethodDef lock_methods
+Modules/_threadmodule.c        -       Locktype        variable        static PyTypeObject Locktype
+Objects/longobject.c   PyLong_FromString       log_base_BASE   variable        static double log_base_BASE[37]
+Objects/longobject.c   -       long_as_number  variable        static PyNumberMethods long_as_number
+Objects/longobject.c   -       long_getset     variable        static PyGetSetDef long_getset[]
+Objects/longobject.c   -       long_methods    variable        static PyMethodDef long_methods
+Objects/rangeobject.c  -       longrangeiter_methods   variable        static PyMethodDef longrangeiter_methods
+Modules/_functoolsmodule.c     -       lru_cache_getsetlist    variable        static PyGetSetDef lru_cache_getsetlist[]
+Modules/_functoolsmodule.c     -       lru_cache_methods       variable        static PyMethodDef lru_cache_methods
+Modules/_functoolsmodule.c     -       lru_cache_type  variable        static PyTypeObject lru_cache_type
+Modules/_functoolsmodule.c     -       lru_list_elem_type      variable        static PyTypeObject lru_list_elem_type
+Python/Python-ast.c    -       LShift_singleton        variable        static PyObject *LShift_singleton
+Python/Python-ast.c    -       LShift_type     variable        static PyTypeObject *LShift_type
+Python/Python-ast.c    -       LtE_singleton   variable        static PyObject *LtE_singleton
+Python/Python-ast.c    -       LtE_type        variable        static PyTypeObject *LtE_type
+Python/Python-ast.c    -       Lt_singleton    variable        static PyObject *Lt_singleton
+Python/Python-ast.c    -       Lt_type variable        static PyTypeObject *Lt_type
+Python/bltinmodule.c   -       map_methods     variable        static PyMethodDef map_methods
+Objects/descrobject.c  -       mappingproxy_as_mapping variable        static PyMappingMethods mappingproxy_as_mapping
+Objects/descrobject.c  -       mappingproxy_as_sequence        variable        static PySequenceMethods mappingproxy_as_sequence
+Objects/descrobject.c  -       mappingproxy_methods    variable        static PyMethodDef mappingproxy_methods
+Objects/dictobject.c   -       mapp_methods    variable        static PyMethodDef mapp_methods
+Python/marshal.c       -       marshal_methods variable        static PyMethodDef marshal_methods
+Python/marshal.c       -       marshalmodule   variable        static struct PyModuleDef marshalmodule
+Modules/_sre.c -       match_as_mapping        variable        static PyMappingMethods match_as_mapping
+Modules/_sre.c -       match_getset    variable        static PyGetSetDef match_getset[]
+Modules/_sre.c -       match_members   variable        static PyMemberDef match_members[]
+Modules/_sre.c -       match_methods   variable        static PyMethodDef match_methods
+Modules/_sre.c -       Match_Type      variable        static PyTypeObject Match_Type
+Python/Python-ast.c    -       MatMult_singleton       variable        static PyObject *MatMult_singleton
+Python/Python-ast.c    -       MatMult_type    variable        static PyTypeObject *MatMult_type
+Objects/obmalloc.c     -       maxarenas       variable        static uint maxarenas
+Objects/moduleobject.c -       max_module_number       variable        static Py_ssize_t max_module_number
+Objects/descrobject.c  -       member_getset   variable        static PyGetSetDef member_getset[]
+Objects/exceptions.c   -       memerrors_freelist      variable        static PyBaseExceptionObject *memerrors_freelist
+Objects/exceptions.c   -       memerrors_numfree       variable        static int memerrors_numfree
+Objects/memoryobject.c -       memory_as_buffer        variable        static PyBufferProcs memory_as_buffer
+Objects/memoryobject.c -       memory_as_mapping       variable        static PyMappingMethods memory_as_mapping
+Objects/memoryobject.c -       memory_as_sequence      variable        static PySequenceMethods memory_as_sequence
+Objects/memoryobject.c -       memory_getsetlist       variable        static PyGetSetDef memory_getsetlist[]
+Objects/memoryobject.c -       memory_methods  variable        static PyMethodDef memory_methods
+Objects/methodobject.c -       meth_getsets    variable        static PyGetSetDef meth_getsets []
+Objects/methodobject.c -       meth_members    variable        static PyMemberDef meth_members[]
+Objects/methodobject.c -       meth_methods    variable        static PyMethodDef meth_methods
+Objects/typeobject.c   -       method_cache    variable        static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP]
+Modules/_operator.c    -       methodcaller_methods    variable        static PyMethodDef methodcaller_methods
+Modules/_operator.c    -       methodcaller_type       variable        static PyTypeObject methodcaller_type
+Objects/classobject.c  -       method_getset   variable        static PyGetSetDef method_getset[]
+Objects/descrobject.c  -       method_getset   variable        static PyGetSetDef method_getset[]
+Objects/classobject.c  -       method_memberlist       variable        static PyMemberDef method_memberlist[]
+Objects/classobject.c  -       method_methods  variable        static PyMethodDef method_methods
+Python/codecs.c        _PyCodecRegistry_Init   methods variable        static struct { char *name; PyMethodDef def; } methods[]
+Python/frozen.c        -       M___hello__     variable        static unsigned char M___hello__[]
+Python/Python-ast.c    -       Mod_singleton   variable        static PyObject *Mod_singleton
+Python/Python-ast.c    -       mod_type        variable        static PyTypeObject *mod_type
+Python/Python-ast.c    -       Mod_type        variable        static PyTypeObject *Mod_type
+Modules/faulthandler.c -       module_def      variable        static struct PyModuleDef module_def
+Modules/_tracemalloc.c -       module_def      variable        static struct PyModuleDef module_def
+Python/Python-ast.c    -       Module_fields   variable        static const char *Module_fields[]
+Modules/_collectionsmodule.c   -       module_functions        variable        static struct PyMethodDef module_functions[]
+Modules/_abc.c -       module_functions        variable        static struct PyMethodDef module_functions[]
+Objects/moduleobject.c -       module_members  variable        static PyMemberDef module_members[]
+Objects/moduleobject.c -       module_methods  variable        static PyMethodDef module_methods
+Modules/_functoolsmodule.c     -       module_methods  variable        static PyMethodDef module_methods
+Modules/itertoolsmodule.c      -       module_methods  variable        static PyMethodDef module_methods
+Modules/_io/_iomodule.c        -       module_methods  variable        static PyMethodDef module_methods
+Modules/faulthandler.c -       module_methods  variable        static PyMethodDef module_methods
+Modules/_tracemalloc.c -       module_methods  variable        static PyMethodDef module_methods
+Python/Python-ast.c    -       Module_type     variable        static PyTypeObject *Module_type
+Python/Python-ast.c    -       Mult_singleton  variable        static PyObject *Mult_singleton
+Python/Python-ast.c    -       Mult_type       variable        static PyTypeObject *Mult_type
+Objects/funcobject.c   PyFunction_NewWithQualName      __name__        variable        static PyObject *__name__
+Python/compile.c       compiler_lambda name    variable        static identifier name
+Python/compile.c       compiler_genexp name    variable        static identifier name
+Python/compile.c       compiler_listcomp       name    variable        static identifier name
+Python/compile.c       compiler_setcomp        name    variable        static identifier name
+Python/compile.c       compiler_dictcomp       name    variable        static identifier name
+Python/Python-ast.c    -       NamedExpr_fields        variable        static const char *NamedExpr_fields[]
+Python/Python-ast.c    -       NamedExpr_type  variable        static PyTypeObject *NamedExpr_type
+Python/Python-ast.c    -       Name_fields     variable        static const char *Name_fields[]
+Objects/typeobject.c   -       name_op variable        static _Py_Identifier name_op[]
+Objects/namespaceobject.c      -       namespace_members       variable        static PyMemberDef namespace_members[]
+Objects/namespaceobject.c      -       namespace_methods       variable        static PyMethodDef namespace_methods
+Python/Python-ast.c    -       Name_type       variable        static PyTypeObject *Name_type
+Objects/obmalloc.c     -       narenas_currently_allocated     variable        static size_t narenas_currently_allocated
+Objects/obmalloc.c     -       narenas_highwater       variable        static size_t narenas_highwater
+Python/sysmodule.c     sys_displayhook newline variable        static PyObject *newline
+Objects/typeobject.c   -       next_version_tag        variable        static unsigned int next_version_tag
+Objects/obmalloc.c     -       nfp2lasta       variable        static struct arena_object* nfp2lasta[MAX_POOLS_IN_ARENA + 1]
+Python/dynload_shlib.c -       nhandles        variable        static int nhandles
+Objects/object.c       -       none_as_number  variable        static PyNumberMethods none_as_number
+Python/Python-ast.c    -       Nonlocal_fields variable        static const char *Nonlocal_fields[]
+Python/Python-ast.c    -       Nonlocal_type   variable        static PyTypeObject *Nonlocal_type
+Python/Python-ast.c    -       NotEq_singleton variable        static PyObject *NotEq_singleton
+Python/Python-ast.c    -       NotEq_type      variable        static PyTypeObject *NotEq_type
+Objects/object.c       -       notimplemented_methods  variable        static PyMethodDef notimplemented_methods
+Python/Python-ast.c    -       NotIn_singleton variable        static PyObject *NotIn_singleton
+Python/Python-ast.c    -       NotIn_type      variable        static PyTypeObject *NotIn_type
+Python/Python-ast.c    -       Not_singleton   variable        static PyObject *Not_singleton
+Python/Python-ast.c    -       Not_type        variable        static PyTypeObject *Not_type
+Objects/obmalloc.c     -       ntimes_arena_allocated  variable        static size_t ntimes_arena_allocated
+Objects/bytesobject.c  -       nullstring      variable        static PyBytesObject *nullstring
+Objects/codeobject.c   PyCode_NewEmpty nulltuple       variable        static PyObject *nulltuple
+Objects/floatobject.c  -       numfree variable        static int numfree
+Objects/frameobject.c  -       numfree variable        static int numfree
+Objects/listobject.c   -       numfree variable        static int numfree
+Objects/dictobject.c   -       numfree variable        static int numfree
+Objects/methodobject.c -       numfree variable        static int numfree
+Objects/tupleobject.c  -       numfree variable        static int numfree[PyTuple_MAXSAVESIZE]
+Objects/classobject.c  -       numfree variable        static int numfree
+Modules/_collectionsmodule.c   -       numfreeblocks   variable        static Py_ssize_t numfreeblocks
+Objects/dictobject.c   -       numfreekeys     variable        static int numfreekeys
+Objects/typeobject.c   -       object_getsets  variable        static PyGetSetDef object_getsets[]
+Objects/typeobject.c   -       object_methods  variable        static PyMethodDef object_methods
+Objects/typeobject.c   object___reduce_ex___impl       objreduce       variable        static PyObject *objreduce
+Objects/odictobject.c  -       odict_as_mapping        variable        static PyMappingMethods odict_as_mapping
+Objects/odictobject.c  -       odict_getset    variable        static PyGetSetDef odict_getset[]
+Objects/odictobject.c  -       odictitems_methods      variable        static PyMethodDef odictitems_methods
+Objects/odictobject.c  -       odictiter_methods       variable        static PyMethodDef odictiter_methods
+Objects/odictobject.c  -       odictkeys_methods       variable        static PyMethodDef odictkeys_methods
+Objects/odictobject.c  -       odict_methods   variable        static PyMethodDef odict_methods
+Objects/odictobject.c  -       odictvalues_methods     variable        static PyMethodDef odictvalues_methods
+Modules/faulthandler.c -       old_stack       variable        static stack_t old_stack
+Modules/_operator.c    -       operator_methods        variable        static PyMethodDef operator_methods
+Modules/_operator.c    -       operatormodule  variable        static struct PyModuleDef operatormodule
+Python/Python-ast.c    -       operator_type   variable        static PyTypeObject *operator_type
+Objects/typeobject.c   slot_nb_add     op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_subtract        op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_multiply        op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_matrix_multiply op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_remainder       op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_divmod  op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_power_binary    op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_lshift  op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_rshift  op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_and     op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_xor     op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_or      op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_floor_divide    op_id   variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_true_divide     op_id   variable        _Py_static_string(op_id, OPSTR)
+Python/getopt.c        -       opt_ptr variable        static const wchar_t *opt_ptr
+Python/initconfig.c    -       orig_argv       variable        static PyWideStringList orig_argv
+Python/Python-ast.c    -       Or_singleton    variable        static PyObject *Or_singleton
+Python/Python-ast.c    -       Or_type variable        static PyTypeObject *Or_type
+Objects/exceptions.c   -       OSError_getset  variable        static PyGetSetDef OSError_getset[]
+Objects/exceptions.c   -       OSError_members variable        static PyMemberDef OSError_members[]
+Objects/exceptions.c   -       OSError_methods variable        static PyMethodDef OSError_methods
+Python/dtoa.c  -       p5s     variable        static Bigint *p5s
+Python/Python-ast.c    -       Param_singleton variable        static PyObject *Param_singleton
+Python/Python-ast.c    -       Param_type      variable        static PyTypeObject *Param_type
+Python/bltinmodule.c   builtin_print   _parser variable        static struct _PyArg_Parser _parser
+Python/clinic/_warnings.c.h    warnings_warn   _parser variable        static _PyArg_Parser _parser
+Python/clinic/bltinmodule.c.h  builtin_compile _parser variable        static _PyArg_Parser _parser
+Python/clinic/bltinmodule.c.h  builtin_round   _parser variable        static _PyArg_Parser _parser
+Python/clinic/bltinmodule.c.h  builtin_sum     _parser variable        static _PyArg_Parser _parser
+Python/clinic/import.c.h       _imp_source_hash        _parser variable        static _PyArg_Parser _parser
+Python/clinic/sysmodule.c.h    sys_addaudithook        _parser variable        static _PyArg_Parser _parser
+Python/clinic/sysmodule.c.h    sys_set_coroutine_origin_tracking_depth _parser variable        static _PyArg_Parser _parser
+Python/clinic/traceback.c.h    tb_new  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_translate     _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_split _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_rsplit        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_decode        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_splitlines    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytearrayobject.c.h     bytearray_hex   _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_split     _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_rsplit    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_translate _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_decode    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_splitlines        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/bytesobject.c.h bytes_hex       _parser variable        static _PyArg_Parser _parser
+Objects/clinic/codeobject.c.h  code_replace    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/complexobject.c.h       complex_new     _parser variable        static _PyArg_Parser _parser
+Objects/clinic/descrobject.c.h mappingproxy_new        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/descrobject.c.h property_init   _parser variable        static _PyArg_Parser _parser
+Objects/clinic/enumobject.c.h  enum_new        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/funcobject.c.h  func_new        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/listobject.c.h  list_sort       _parser variable        static _PyArg_Parser _parser
+Objects/clinic/longobject.c.h  long_new        _parser variable        static _PyArg_Parser _parser
+Objects/clinic/longobject.c.h  int_to_bytes    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/longobject.c.h  int_from_bytes  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/memoryobject.c.h        memoryview_hex  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/moduleobject.c.h        module___init__ _parser variable        static _PyArg_Parser _parser
+Objects/clinic/odictobject.c.h OrderedDict_fromkeys    _parser variable        static _PyArg_Parser _parser
+Objects/clinic/odictobject.c.h OrderedDict_setdefault  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/odictobject.c.h OrderedDict_popitem     _parser variable        static _PyArg_Parser _parser
+Objects/clinic/odictobject.c.h OrderedDict_move_to_end _parser variable        static _PyArg_Parser _parser
+Objects/clinic/structseq.c.h   structseq_new   _parser variable        static _PyArg_Parser _parser
+Objects/clinic/unicodeobject.c.h       unicode_encode  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/unicodeobject.c.h       unicode_expandtabs      _parser variable        static _PyArg_Parser _parser
+Objects/clinic/unicodeobject.c.h       unicode_split   _parser variable        static _PyArg_Parser _parser
+Objects/clinic/unicodeobject.c.h       unicode_rsplit  _parser variable        static _PyArg_Parser _parser
+Objects/clinic/unicodeobject.c.h       unicode_splitlines      _parser variable        static _PyArg_Parser _parser
+Objects/stringlib/clinic/transmogrify.h.h      stringlib_expandtabs    _parser variable        static _PyArg_Parser _parser
+Modules/_blake2/clinic/blake2b_impl.c.h        py_blake2b_new  _parser variable        static _PyArg_Parser _parser
+Modules/_blake2/clinic/blake2s_impl.c.h        py_blake2s_new  _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/_iomodule.c.h       _io_open        _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/_iomodule.c.h       _io_open_code   _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/bufferedio.c.h      _io_BufferedReader___init__     _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/bufferedio.c.h      _io_BufferedWriter___init__     _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/bufferedio.c.h      _io_BufferedRandom___init__     _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/bytesio.c.h _io_BytesIO___init__    _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/fileio.c.h  _io_FileIO___init__     _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/stringio.c.h        _io_StringIO___init__   _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/textio.c.h  _io_IncrementalNewlineDecoder___init__  _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/textio.c.h  _io_IncrementalNewlineDecoder_decode    _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/textio.c.h  _io_TextIOWrapper___init__      _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/textio.c.h  _io_TextIOWrapper_reconfigure   _parser variable        static _PyArg_Parser _parser
+Modules/_io/clinic/winconsoleio.c.h    _io__WindowsConsoleIO___init__  _parser variable        static _PyArg_Parser _parser
+Modules/_multiprocessing/clinic/posixshmem.c.h _posixshmem_shm_open    _parser variable        static _PyArg_Parser _parser
+Modules/_multiprocessing/clinic/posixshmem.c.h _posixshmem_shm_unlink  _parser variable        static _PyArg_Parser _parser
+Modules/cjkcodecs/clinic/multibytecodec.c.h    _multibytecodec_MultibyteCodec_encode   _parser variable        static _PyArg_Parser _parser
+Modules/cjkcodecs/clinic/multibytecodec.c.h    _multibytecodec_MultibyteCodec_decode   _parser variable        static _PyArg_Parser _parser
+Modules/cjkcodecs/clinic/multibytecodec.c.h    _multibytecodec_MultibyteIncrementalEncoder_encode      _parser variable        static _PyArg_Parser _parser
+Modules/cjkcodecs/clinic/multibytecodec.c.h    _multibytecodec_MultibyteIncrementalDecoder_decode      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Future___init__        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Future_add_done_callback       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Task___init__  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Task_current_task      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Task_all_tasks _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Task_get_stack _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio_Task_print_stack       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio__register_task _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio__unregister_task       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio__enter_task    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_asynciomodule.c.h      _asyncio__leave_task    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_bz2module.c.h  _bz2_BZ2Decompressor_decompress _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_codecsmodule.c.h       _codecs_encode  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_codecsmodule.c.h       _codecs_decode  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_cursesmodule.c.h       _curses_setupterm       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_datetimemodule.c.h     datetime_datetime_now   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_find       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_findtext   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_findall    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_iterfind   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_get        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_iter       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_Element_getiterator        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_TreeBuilder___init__       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_elementtree.c.h        _elementtree_XMLParser___init__ _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_hashopenssl.c.h        EVP_new _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_hashopenssl.c.h        pbkdf2_hmac     _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_hashopenssl.c.h        _hashlib_scrypt _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_hashopenssl.c.h        _hashlib_hmac_digest    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_lzmamodule.c.h _lzma_LZMADecompressor_decompress       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_lzmamodule.c.h _lzma_LZMADecompressor___init__ _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_opcode.c.h     _opcode_stack_effect    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_Pickler___init__        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_Unpickler___init__      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_dump    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_dumps   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_load    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_pickle.c.h     _pickle_loads   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_queuemodule.c.h        _queue_SimpleQueue_put  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_queuemodule.c.h        _queue_SimpleQueue_put_nowait   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_queuemodule.c.h        _queue_SimpleQueue_get  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_match  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_fullmatch      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_search _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_findall        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_finditer       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_scanner        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_split  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_sub    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Pattern_subn   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_compile    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Match_expand   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Match_groups   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_sre.c.h        _sre_SRE_Match_groupdict        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLSocket_get_channel_binding     _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLContext_load_cert_chain        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLContext_load_verify_locations  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLContext__wrap_socket   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLContext__wrap_bio      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl__SSLContext_get_ca_certs   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl_txt2obj    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl_enum_certificates  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_ssl.c.h        _ssl_enum_crls  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_struct.c.h     Struct___init__ _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_struct.c.h     Struct_unpack_from      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_struct.c.h     unpack_from     _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_winapi.c.h     _winapi_ConnectNamedPipe        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_winapi.c.h     _winapi_ReadFile        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_winapi.c.h     _winapi_WriteFile       _parser variable        static _PyArg_Parser _parser
+Modules/clinic/_winapi.c.h     _winapi_GetFileType     _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_b2a_uu _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_b2a_base64     _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_b2a_hex        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_hexlify        _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_a2b_qp _parser variable        static _PyArg_Parser _parser
+Modules/clinic/binascii.c.h    binascii_b2a_qp _parser variable        static _PyArg_Parser _parser
+Modules/clinic/cmathmodule.c.h cmath_isclose   _parser variable        static _PyArg_Parser _parser
+Modules/clinic/gcmodule.c.h    gc_collect      _parser variable        static _PyArg_Parser _parser
+Modules/clinic/gcmodule.c.h    gc_get_objects  _parser variable        static _PyArg_Parser _parser
+Modules/clinic/grpmodule.c.h   grp_getgrgid    _parser variable        static _PyArg_Parser _parser
+Modules/clinic/grpmodule.c.h   grp_getgrnam    _parser variable        static _PyArg_Parser _parser
+Modules/_functoolsmodule.c     -       partial_getsetlist      variable        static PyGetSetDef partial_getsetlist[]
+Modules/_functoolsmodule.c     -       partial_memberlist      variable        static PyMemberDef partial_memberlist[]
+Modules/_functoolsmodule.c     -       partial_methods variable        static PyMethodDef partial_methods
+Modules/_functoolsmodule.c     -       partial_type    variable        static PyTypeObject partial_type
+Python/Python-ast.c    -       Pass_type       variable        static PyTypeObject *Pass_type
+Modules/_sre.c -       pattern_getset  variable        static PyGetSetDef pattern_getset[]
+Modules/_sre.c -       pattern_members variable        static PyMemberDef pattern_members[]
+Modules/_sre.c -       pattern_methods variable        static PyMethodDef pattern_methods
+Modules/_sre.c -       Pattern_Type    variable        static PyTypeObject Pattern_Type
+Modules/itertoolsmodule.c      -       permuations_methods     variable        static PyMethodDef permuations_methods
+Modules/itertoolsmodule.c      -       permutations_type       variable        static PyTypeObject permutations_type
+Objects/picklebufobject.c      -       picklebuf_as_buffer     variable        static PyBufferProcs picklebuf_as_buffer
+Objects/picklebufobject.c      -       picklebuf_methods       variable        static PyMethodDef picklebuf_methods
+Python/dtoa.c  -       pmem_next       variable        static double *pmem_next
+Objects/typeobject.c   resolve_slotdups        pname   variable        static PyObject *pname
+Modules/posixmodule.c  -       posix_constants_confstr variable        static struct constdef posix_constants_confstr[]
+Modules/posixmodule.c  -       posix_constants_pathconf        variable        static struct constdef  posix_constants_pathconf[]
+Modules/posixmodule.c  -       posix_constants_sysconf variable        static struct constdef posix_constants_sysconf[]
+Modules/posixmodule.c  -       posix_methods   variable        static PyMethodDef posix_methods
+Modules/posixmodule.c  -       posixmodule     variable        static struct PyModuleDef posixmodule
+Modules/posixmodule.c  -       posix_putenv_garbage    variable        static PyObject *posix_putenv_garbage
+Python/Python-ast.c    -       Pow_singleton   variable        static PyObject *Pow_singleton
+Python/Python-ast.c    -       Pow_type        variable        static PyTypeObject *Pow_type
+Python/sysmodule.c     -       _preinit_warnoptions    variable        static _Py_PreInitEntry _preinit_warnoptions
+Python/sysmodule.c     -       _preinit_xoptions       variable        static _Py_PreInitEntry _preinit_xoptions
+Objects/exceptions.c   _check_for_legacy_statements    print_prefix    variable        static PyObject *print_prefix
+Python/dtoa.c  -       private_mem     variable        static double private_mem[PRIVATE_mem]
+Modules/itertoolsmodule.c      -       product_methods variable        static PyMethodDef product_methods
+Modules/itertoolsmodule.c      -       product_type    variable        static PyTypeObject product_type
+Objects/descrobject.c  -       property_getsetlist     variable        static PyGetSetDef property_getsetlist[]
+Objects/descrobject.c  -       property_members        variable        static PyMemberDef property_members[]
+Objects/descrobject.c  -       property_methods        variable        static PyMethodDef property_methods
+Objects/weakrefobject.c        -       proxy_as_mapping        variable        static PyMappingMethods proxy_as_mapping
+Objects/weakrefobject.c        -       proxy_as_number variable        static PyNumberMethods proxy_as_number
+Objects/weakrefobject.c        -       proxy_as_sequence       variable        static PySequenceMethods proxy_as_sequence
+Objects/weakrefobject.c        -       proxy_methods   variable        static PyMethodDef proxy_methods
+Objects/typeobject.c   resolve_slotdups        ptrs    variable        static slotdef *ptrs[MAX_EQUIV]
+Modules/pwdmodule.c    -       pwd_methods     variable        static PyMethodDef pwd_methods
+Modules/pwdmodule.c    -       pwdmodule       variable        static struct PyModuleDef pwdmodule
+Objects/obmalloc.c     -       _Py_AllocatedBlocks     variable        static Py_ssize_t _Py_AllocatedBlocks
+Objects/genobject.c    -       _PyAsyncGenASend_Type   variable        PyTypeObject _PyAsyncGenASend_Type
+Objects/genobject.c    -       _PyAsyncGenAThrow_Type  variable        PyTypeObject _PyAsyncGenAThrow_Type
+Objects/genobject.c    -       PyAsyncGen_Type variable        PyTypeObject PyAsyncGen_Type
+Objects/genobject.c    -       _PyAsyncGenWrappedValue_Type    variable        PyTypeObject _PyAsyncGenWrappedValue_Type
+Objects/typeobject.c   -       PyBaseObject_Type       variable        PyTypeObject PyBaseObject_Type
+Objects/boolobject.c   -       PyBool_Type     variable        PyTypeObject PyBool_Type
+Modules/_io/bufferedio.c       -       PyBufferedIOBase_Type   variable        PyTypeObject PyBufferedIOBase_Type
+Modules/_io/bufferedio.c       -       PyBufferedRandom_Type   variable        PyTypeObject PyBufferedRandom_Type
+Modules/_io/bufferedio.c       -       PyBufferedReader_Type   variable        PyTypeObject PyBufferedReader_Type
+Modules/_io/bufferedio.c       -       PyBufferedRWPair_Type   variable        PyTypeObject PyBufferedRWPair_Type
+Modules/_io/bufferedio.c       -       PyBufferedWriter_Type   variable        PyTypeObject PyBufferedWriter_Type
+Objects/bytearrayobject.c      -       _PyByteArray_empty_string       variable        char _PyByteArray_empty_string[]
+Objects/bytearrayobject.c      -       PyByteArrayIter_Type    variable        PyTypeObject PyByteArrayIter_Type
+Objects/bytearrayobject.c      -       PyByteArray_Type        variable        PyTypeObject PyByteArray_Type
+Modules/_io/bytesio.c  -       _PyBytesIOBuffer_Type   variable        PyTypeObject _PyBytesIOBuffer_Type
+Modules/_io/bytesio.c  -       PyBytesIO_Type  variable        PyTypeObject PyBytesIO_Type
+Objects/bytesobject.c  -       PyBytesIter_Type        variable        PyTypeObject PyBytesIter_Type
+Objects/bytesobject.c  -       PyBytes_Type    variable        PyTypeObject PyBytes_Type
+Python/initconfig.c    -       Py_BytesWarningFlag     variable        int Py_BytesWarningFlag
+Objects/iterobject.c   -       PyCallIter_Type variable        PyTypeObject PyCallIter_Type
+Objects/capsule.c      -       PyCapsule_Type  variable        PyTypeObject PyCapsule_Type
+Objects/cellobject.c   -       PyCell_Type     variable        PyTypeObject PyCell_Type
+Objects/methodobject.c -       PyCFunction_Type        variable        PyTypeObject PyCFunction_Type
+Python/ceval.c -       _Py_CheckRecursionLimit variable        int _Py_CheckRecursionLimit
+Objects/descrobject.c  -       PyClassMethodDescr_Type variable        PyTypeObject PyClassMethodDescr_Type
+Objects/funcobject.c   -       PyClassMethod_Type      variable        PyTypeObject PyClassMethod_Type
+Objects/codeobject.c   -       PyCode_Type     variable        PyTypeObject PyCode_Type
+Objects/complexobject.c        -       PyComplex_Type  variable        PyTypeObject PyComplex_Type
+Python/context.c       -       PyContext_as_mapping    variable        static PyMappingMethods PyContext_as_mapping
+Python/context.c       -       PyContext_as_sequence   variable        static PySequenceMethods PyContext_as_sequence
+Python/context.c       -       PyContext_methods       variable        static PyMethodDef PyContext_methods
+Python/context.c       -       PyContextTokenMissing_Type      variable        PyTypeObject PyContextTokenMissing_Type
+Python/context.c       -       PyContextToken_Type     variable        PyTypeObject PyContextToken_Type
+Python/context.c       -       PyContextTokenType_getsetlist   variable        static PyGetSetDef PyContextTokenType_getsetlist[]
+Python/context.c       -       PyContext_Type  variable        PyTypeObject PyContext_Type
+Python/context.c       -       PyContextVar_members    variable        static PyMemberDef PyContextVar_members[]
+Python/context.c       -       PyContextVar_methods    variable        static PyMethodDef PyContextVar_methods
+Python/context.c       -       PyContextVar_Type       variable        PyTypeObject PyContextVar_Type
+Objects/genobject.c    -       PyCoro_Type     variable        PyTypeObject PyCoro_Type
+Objects/genobject.c    -       _PyCoroWrapper_Type     variable        PyTypeObject _PyCoroWrapper_Type
+Python/initconfig.c    -       Py_DebugFlag    variable        int Py_DebugFlag
+Objects/dictobject.c   -       pydict_global_version   variable        static uint64_t pydict_global_version
+Objects/dictobject.c   -       PyDictItems_Type        variable        PyTypeObject PyDictItems_Type
+Objects/dictobject.c   -       PyDictIterItem_Type     variable        PyTypeObject PyDictIterItem_Type
+Objects/dictobject.c   -       PyDictIterKey_Type      variable        PyTypeObject PyDictIterKey_Type
+Objects/dictobject.c   -       PyDictIterValue_Type    variable        PyTypeObject PyDictIterValue_Type
+Objects/dictobject.c   -       PyDictKeys_Type variable        PyTypeObject PyDictKeys_Type
+Objects/descrobject.c  -       PyDictProxy_Type        variable        PyTypeObject PyDictProxy_Type
+Objects/dictobject.c   -       PyDictRevIterItem_Type  variable        PyTypeObject PyDictRevIterItem_Type
+Objects/dictobject.c   -       PyDictRevIterKey_Type   variable        PyTypeObject PyDictRevIterKey_Type
+Objects/dictobject.c   -       PyDictRevIterValue_Type variable        PyTypeObject PyDictRevIterValue_Type
+Objects/dictobject.c   -       PyDict_Type     variable        PyTypeObject PyDict_Type
+Objects/dictobject.c   -       PyDictValues_Type       variable        PyTypeObject PyDictValues_Type
+Python/initconfig.c    -       Py_DontWriteBytecodeFlag        variable        int Py_DontWriteBytecodeFlag
+Objects/sliceobject.c  -       _Py_EllipsisObject      variable        PyObject _Py_EllipsisObject
+Objects/sliceobject.c  -       PyEllipsis_Type variable        PyTypeObject PyEllipsis_Type
+Objects/enumobject.c   -       PyEnum_Type     variable        PyTypeObject PyEnum_Type
+Objects/exceptions.c   -       _PyExc_ArithmeticError  variable        static PyTypeObject _PyExc_ArithmeticError
+Objects/exceptions.c   -       PyExc_ArithmeticError   variable        static PyTypeObject PyExc_ArithmeticError
+Objects/exceptions.c   -       _PyExc_AssertionError   variable        static PyTypeObject _PyExc_AssertionError
+Objects/exceptions.c   -       PyExc_AssertionError    variable        static PyTypeObject PyExc_AssertionError
+Objects/exceptions.c   -       _PyExc_AttributeError   variable        static PyTypeObject _PyExc_AttributeError
+Objects/exceptions.c   -       PyExc_AttributeError    variable        static PyTypeObject PyExc_AttributeError
+Objects/exceptions.c   -       _PyExc_BaseException    variable        static PyTypeObject _PyExc_BaseException
+Objects/exceptions.c   -       PyExc_BaseException     variable        static PyTypeObject PyExc_BaseException
+Objects/exceptions.c   -       _PyExc_BlockingIOError  variable        static PyTypeObject _PyExc_BlockingIOError
+Objects/exceptions.c   -       PyExc_BlockingIOError   variable        static PyTypeObject PyExc_BlockingIOError
+Objects/exceptions.c   -       _PyExc_BrokenPipeError  variable        static PyTypeObject _PyExc_BrokenPipeError
+Objects/exceptions.c   -       PyExc_BrokenPipeError   variable        static PyTypeObject PyExc_BrokenPipeError
+Objects/exceptions.c   -       _PyExc_BufferError      variable        static PyTypeObject _PyExc_BufferError
+Objects/exceptions.c   -       PyExc_BufferError       variable        static PyTypeObject PyExc_BufferError
+Objects/exceptions.c   -       _PyExc_BytesWarning     variable        static PyTypeObject _PyExc_BytesWarning
+Objects/exceptions.c   -       PyExc_BytesWarning      variable        static PyTypeObject PyExc_BytesWarning
+Objects/exceptions.c   -       _PyExc_ChildProcessError        variable        static PyTypeObject _PyExc_ChildProcessError
+Objects/exceptions.c   -       PyExc_ChildProcessError variable        static PyTypeObject PyExc_ChildProcessError
+Objects/exceptions.c   -       _PyExc_ConnectionAbortedError   variable        static PyTypeObject _PyExc_ConnectionAbortedError
+Objects/exceptions.c   -       PyExc_ConnectionAbortedError    variable        static PyTypeObject PyExc_ConnectionAbortedError
+Objects/exceptions.c   -       _PyExc_ConnectionError  variable        static PyTypeObject _PyExc_ConnectionError
+Objects/exceptions.c   -       PyExc_ConnectionError   variable        static PyTypeObject PyExc_ConnectionError
+Objects/exceptions.c   -       _PyExc_ConnectionRefusedError   variable        static PyTypeObject _PyExc_ConnectionRefusedError
+Objects/exceptions.c   -       PyExc_ConnectionRefusedError    variable        static PyTypeObject PyExc_ConnectionRefusedError
+Objects/exceptions.c   -       _PyExc_ConnectionResetError     variable        static PyTypeObject _PyExc_ConnectionResetError
+Objects/exceptions.c   -       PyExc_ConnectionResetError      variable        static PyTypeObject PyExc_ConnectionResetError
+Objects/exceptions.c   -       _PyExc_DeprecationWarning       variable        static PyTypeObject _PyExc_DeprecationWarning
+Objects/exceptions.c   -       PyExc_DeprecationWarning        variable        static PyTypeObject PyExc_DeprecationWarning
+Objects/exceptions.c   -       PyExc_EnvironmentError  variable        static PyTypeObject PyExc_EnvironmentError
+Objects/exceptions.c   -       _PyExc_EOFError variable        static PyTypeObject _PyExc_EOFError
+Objects/exceptions.c   -       PyExc_EOFError  variable        static PyTypeObject PyExc_EOFError
+Objects/exceptions.c   -       _PyExc_Exception        variable        static PyTypeObject _PyExc_Exception
+Objects/exceptions.c   -       PyExc_Exception variable        static PyTypeObject PyExc_Exception
+Objects/exceptions.c   -       _PyExc_FileExistsError  variable        static PyTypeObject _PyExc_FileExistsError
+Objects/exceptions.c   -       PyExc_FileExistsError   variable        static PyTypeObject PyExc_FileExistsError
+Objects/exceptions.c   -       _PyExc_FileNotFoundError        variable        static PyTypeObject _PyExc_FileNotFoundError
+Objects/exceptions.c   -       PyExc_FileNotFoundError variable        static PyTypeObject PyExc_FileNotFoundError
+Objects/exceptions.c   -       _PyExc_FloatingPointError       variable        static PyTypeObject _PyExc_FloatingPointError
+Objects/exceptions.c   -       PyExc_FloatingPointError        variable        static PyTypeObject PyExc_FloatingPointError
+Objects/exceptions.c   -       _PyExc_FutureWarning    variable        static PyTypeObject _PyExc_FutureWarning
+Objects/exceptions.c   -       PyExc_FutureWarning     variable        static PyTypeObject PyExc_FutureWarning
+Objects/exceptions.c   -       _PyExc_GeneratorExit    variable        static PyTypeObject _PyExc_GeneratorExit
+Objects/exceptions.c   -       PyExc_GeneratorExit     variable        static PyTypeObject PyExc_GeneratorExit
+Objects/exceptions.c   -       _PyExc_ImportError      variable        static PyTypeObject _PyExc_ImportError
+Objects/exceptions.c   -       PyExc_ImportError       variable        static PyTypeObject PyExc_ImportError
+Objects/exceptions.c   -       _PyExc_ImportWarning    variable        static PyTypeObject _PyExc_ImportWarning
+Objects/exceptions.c   -       PyExc_ImportWarning     variable        static PyTypeObject PyExc_ImportWarning
+Objects/exceptions.c   -       _PyExc_IndentationError variable        static PyTypeObject _PyExc_IndentationError
+Objects/exceptions.c   -       PyExc_IndentationError  variable        static PyTypeObject PyExc_IndentationError
+Objects/exceptions.c   -       _PyExc_IndexError       variable        static PyTypeObject _PyExc_IndexError
+Objects/exceptions.c   -       PyExc_IndexError        variable        static PyTypeObject PyExc_IndexError
+Objects/exceptions.c   -       _PyExc_InterruptedError variable        static PyTypeObject _PyExc_InterruptedError
+Objects/exceptions.c   -       PyExc_InterruptedError  variable        static PyTypeObject PyExc_InterruptedError
+Objects/exceptions.c   -       PyExc_IOError   variable        static PyTypeObject PyExc_IOError
+Objects/exceptions.c   -       _PyExc_IsADirectoryError        variable        static PyTypeObject _PyExc_IsADirectoryError
+Objects/exceptions.c   -       PyExc_IsADirectoryError variable        static PyTypeObject PyExc_IsADirectoryError
+Objects/exceptions.c   -       _PyExc_KeyboardInterrupt        variable        static PyTypeObject _PyExc_KeyboardInterrupt
+Objects/exceptions.c   -       PyExc_KeyboardInterrupt variable        static PyTypeObject PyExc_KeyboardInterrupt
+Objects/exceptions.c   -       _PyExc_KeyError variable        static PyTypeObject _PyExc_KeyError
+Objects/exceptions.c   -       PyExc_KeyError  variable        static PyTypeObject PyExc_KeyError
+Objects/exceptions.c   -       _PyExc_LookupError      variable        static PyTypeObject _PyExc_LookupError
+Objects/exceptions.c   -       PyExc_LookupError       variable        static PyTypeObject PyExc_LookupError
+Objects/exceptions.c   -       _PyExc_MemoryError      variable        static PyTypeObject _PyExc_MemoryError
+Objects/exceptions.c   -       PyExc_MemoryError       variable        static PyTypeObject PyExc_MemoryError
+Objects/exceptions.c   -       _PyExc_ModuleNotFoundError      variable        static PyTypeObject _PyExc_ModuleNotFoundError
+Objects/exceptions.c   -       PyExc_ModuleNotFoundError       variable        static PyTypeObject PyExc_ModuleNotFoundError
+Objects/exceptions.c   -       _PyExc_NameError        variable        static PyTypeObject _PyExc_NameError
+Objects/exceptions.c   -       PyExc_NameError variable        static PyTypeObject PyExc_NameError
+Objects/exceptions.c   -       _PyExc_NotADirectoryError       variable        static PyTypeObject _PyExc_NotADirectoryError
+Objects/exceptions.c   -       PyExc_NotADirectoryError        variable        static PyTypeObject PyExc_NotADirectoryError
+Objects/exceptions.c   -       _PyExc_NotImplementedError      variable        static PyTypeObject _PyExc_NotImplementedError
+Objects/exceptions.c   -       PyExc_NotImplementedError       variable        static PyTypeObject PyExc_NotImplementedError
+Objects/exceptions.c   -       _PyExc_OSError  variable        static PyTypeObject _PyExc_OSError
+Objects/exceptions.c   -       PyExc_OSError   variable        static PyTypeObject PyExc_OSError
+Objects/exceptions.c   -       _PyExc_OverflowError    variable        static PyTypeObject _PyExc_OverflowError
+Objects/exceptions.c   -       PyExc_OverflowError     variable        static PyTypeObject PyExc_OverflowError
+Objects/exceptions.c   -       _PyExc_PendingDeprecationWarning        variable        static PyTypeObject _PyExc_PendingDeprecationWarning
+Objects/exceptions.c   -       PyExc_PendingDeprecationWarning variable        static PyTypeObject PyExc_PendingDeprecationWarning
+Objects/exceptions.c   -       _PyExc_PermissionError  variable        static PyTypeObject _PyExc_PermissionError
+Objects/exceptions.c   -       PyExc_PermissionError   variable        static PyTypeObject PyExc_PermissionError
+Objects/exceptions.c   -       _PyExc_ProcessLookupError       variable        static PyTypeObject _PyExc_ProcessLookupError
+Objects/exceptions.c   -       PyExc_ProcessLookupError        variable        static PyTypeObject PyExc_ProcessLookupError
+Objects/exceptions.c   -       _PyExc_RecursionError   variable        static PyTypeObject _PyExc_RecursionError
+Objects/exceptions.c   -       PyExc_RecursionError    variable        static PyTypeObject PyExc_RecursionError
+Objects/exceptions.c   -       _PyExc_ReferenceError   variable        static PyTypeObject _PyExc_ReferenceError
+Objects/exceptions.c   -       PyExc_ReferenceError    variable        static PyTypeObject PyExc_ReferenceError
+Objects/exceptions.c   -       _PyExc_ResourceWarning  variable        static PyTypeObject _PyExc_ResourceWarning
+Objects/exceptions.c   -       PyExc_ResourceWarning   variable        static PyTypeObject PyExc_ResourceWarning
+Objects/exceptions.c   -       _PyExc_RuntimeError     variable        static PyTypeObject _PyExc_RuntimeError
+Objects/exceptions.c   -       PyExc_RuntimeError      variable        static PyTypeObject PyExc_RuntimeError
+Objects/exceptions.c   -       _PyExc_RuntimeWarning   variable        static PyTypeObject _PyExc_RuntimeWarning
+Objects/exceptions.c   -       PyExc_RuntimeWarning    variable        static PyTypeObject PyExc_RuntimeWarning
+Objects/exceptions.c   -       _PyExc_StopAsyncIteration       variable        static PyTypeObject _PyExc_StopAsyncIteration
+Objects/exceptions.c   -       PyExc_StopAsyncIteration        variable        static PyTypeObject PyExc_StopAsyncIteration
+Objects/exceptions.c   -       _PyExc_StopIteration    variable        static PyTypeObject _PyExc_StopIteration
+Objects/exceptions.c   -       PyExc_StopIteration     variable        static PyTypeObject PyExc_StopIteration
+Objects/exceptions.c   -       _PyExc_SyntaxError      variable        static PyTypeObject _PyExc_SyntaxError
+Objects/exceptions.c   -       PyExc_SyntaxError       variable        static PyTypeObject PyExc_SyntaxError
+Objects/exceptions.c   -       _PyExc_SyntaxWarning    variable        static PyTypeObject _PyExc_SyntaxWarning
+Objects/exceptions.c   -       PyExc_SyntaxWarning     variable        static PyTypeObject PyExc_SyntaxWarning
+Objects/exceptions.c   -       _PyExc_SystemError      variable        static PyTypeObject _PyExc_SystemError
+Objects/exceptions.c   -       PyExc_SystemError       variable        static PyTypeObject PyExc_SystemError
+Objects/exceptions.c   -       _PyExc_SystemExit       variable        static PyTypeObject _PyExc_SystemExit
+Objects/exceptions.c   -       PyExc_SystemExit        variable        static PyTypeObject PyExc_SystemExit
+Objects/exceptions.c   -       _PyExc_TabError variable        static PyTypeObject _PyExc_TabError
+Objects/exceptions.c   -       PyExc_TabError  variable        static PyTypeObject PyExc_TabError
+Objects/exceptions.c   -       _PyExc_TargetScopeError variable        static PyTypeObject _PyExc_TargetScopeError
+Objects/exceptions.c   -       PyExc_TargetScopeError  variable        static PyTypeObject PyExc_TargetScopeError
+Objects/exceptions.c   -       _PyExc_TimeoutError     variable        static PyTypeObject _PyExc_TimeoutError
+Objects/exceptions.c   -       PyExc_TimeoutError      variable        static PyTypeObject PyExc_TimeoutError
+Objects/exceptions.c   -       _PyExc_TypeError        variable        static PyTypeObject _PyExc_TypeError
+Objects/exceptions.c   -       PyExc_TypeError variable        static PyTypeObject PyExc_TypeError
+Objects/exceptions.c   -       _PyExc_UnboundLocalError        variable        static PyTypeObject _PyExc_UnboundLocalError
+Objects/exceptions.c   -       PyExc_UnboundLocalError variable        static PyTypeObject PyExc_UnboundLocalError
+Objects/exceptions.c   -       _PyExc_UnicodeDecodeError       variable        static PyTypeObject _PyExc_UnicodeDecodeError
+Objects/exceptions.c   -       PyExc_UnicodeDecodeError        variable        static PyTypeObject PyExc_UnicodeDecodeError
+Objects/exceptions.c   -       _PyExc_UnicodeEncodeError       variable        static PyTypeObject _PyExc_UnicodeEncodeError
+Objects/exceptions.c   -       PyExc_UnicodeEncodeError        variable        static PyTypeObject PyExc_UnicodeEncodeError
+Objects/exceptions.c   -       _PyExc_UnicodeError     variable        static PyTypeObject _PyExc_UnicodeError
+Objects/exceptions.c   -       PyExc_UnicodeError      variable        static PyTypeObject PyExc_UnicodeError
+Objects/exceptions.c   -       _PyExc_UnicodeTranslateError    variable        static PyTypeObject _PyExc_UnicodeTranslateError
+Objects/exceptions.c   -       PyExc_UnicodeTranslateError     variable        static PyTypeObject PyExc_UnicodeTranslateError
+Objects/exceptions.c   -       _PyExc_UnicodeWarning   variable        static PyTypeObject _PyExc_UnicodeWarning
+Objects/exceptions.c   -       PyExc_UnicodeWarning    variable        static PyTypeObject PyExc_UnicodeWarning
+Objects/exceptions.c   -       _PyExc_UserWarning      variable        static PyTypeObject _PyExc_UserWarning
+Objects/exceptions.c   -       PyExc_UserWarning       variable        static PyTypeObject PyExc_UserWarning
+Objects/exceptions.c   -       _PyExc_ValueError       variable        static PyTypeObject _PyExc_ValueError
+Objects/exceptions.c   -       PyExc_ValueError        variable        static PyTypeObject PyExc_ValueError
+Objects/exceptions.c   -       _PyExc_Warning  variable        static PyTypeObject _PyExc_Warning
+Objects/exceptions.c   -       PyExc_Warning   variable        static PyTypeObject PyExc_Warning
+Objects/exceptions.c   -       _PyExc_ZeroDivisionError        variable        static PyTypeObject _PyExc_ZeroDivisionError
+Objects/exceptions.c   -       PyExc_ZeroDivisionError variable        static PyTypeObject PyExc_ZeroDivisionError
+Objects/boolobject.c   -       _Py_FalseStruct variable        static struct _longobject _Py_FalseStruct
+Objects/stringlib/unicode_format.h     -       PyFieldNameIter_Type    variable        static PyTypeObject PyFieldNameIter_Type
+Modules/_io/fileio.c   -       PyFileIO_Type   variable        PyTypeObject PyFileIO_Type
+Python/preconfig.c     -       Py_FileSystemDefaultEncodeErrors        variable        const char *Py_FileSystemDefaultEncodeErrors
+Python/preconfig.c     -       Py_FileSystemDefaultEncoding    variable        const char * Py_FileSystemDefaultEncoding
+Python/bltinmodule.c   -       PyFilter_Type   variable        PyTypeObject PyFilter_Type
+Objects/floatobject.c  -       PyFloat_Type    variable        PyTypeObject PyFloat_Type
+Objects/stringlib/unicode_format.h     -       PyFormatterIter_Type    variable        static PyTypeObject PyFormatterIter_Type
+Objects/frameobject.c  -       PyFrame_Type    variable        PyTypeObject PyFrame_Type
+Python/initconfig.c    -       Py_FrozenFlag   variable        int Py_FrozenFlag
+Objects/setobject.c    -       PyFrozenSet_Type        variable        PyTypeObject PyFrozenSet_Type
+Objects/funcobject.c   -       PyFunction_Type variable        PyTypeObject PyFunction_Type
+Objects/genobject.c    -       PyGen_Type      variable        PyTypeObject PyGen_Type
+Objects/descrobject.c  -       PyGetSetDescr_Type      variable        PyTypeObject PyGetSetDescr_Type
+Python/hamt.c  -       _PyHamt_ArrayNode_Type  variable        PyTypeObject _PyHamt_ArrayNode_Type
+Python/hamt.c  -       PyHamt_as_mapping       variable        static PyMappingMethods PyHamt_as_mapping
+Python/hamt.c  -       PyHamt_as_sequence      variable        static PySequenceMethods PyHamt_as_sequence
+Python/hamt.c  -       _PyHamt_BitmapNode_Type variable        PyTypeObject _PyHamt_BitmapNode_Type
+Python/hamt.c  -       _PyHamt_CollisionNode_Type      variable        PyTypeObject _PyHamt_CollisionNode_Type
+Python/hamt.c  -       _PyHamtItems_Type       variable        PyTypeObject _PyHamtItems_Type
+Python/hamt.c  -       PyHamtIterator_as_mapping       variable        static PyMappingMethods PyHamtIterator_as_mapping
+Python/hamt.c  -       _PyHamtKeys_Type        variable        PyTypeObject _PyHamtKeys_Type
+Python/hamt.c  -       PyHamt_methods  variable        static PyMethodDef PyHamt_methods
+Python/hamt.c  -       _PyHamt_Type    variable        PyTypeObject _PyHamt_Type
+Python/hamt.c  -       _PyHamtValues_Type      variable        PyTypeObject _PyHamtValues_Type
+Python/preconfig.c     -       _Py_HasFileSystemDefaultEncodeErrors    variable        const(int) _Py_HasFileSystemDefaultEncodeErrors
+Python/preconfig.c     -       Py_HasFileSystemDefaultEncoding variable        const(int) Py_HasFileSystemDefaultEncoding
+Python/pyhash.c        -       PyHash_Func     variable        static PyHash_FuncDef PyHash_Func
+Python/initconfig.c    -       Py_HashRandomizationFlag        variable        int Py_HashRandomizationFlag
+Python/pyhash.c        -       _Py_HashSecret  variable        _Py_HashSecret_t _Py_HashSecret
+Python/bootstrap_hash.c        -       _Py_HashSecret_Initialized      variable        static int _Py_HashSecret_Initialized
+Python/codecs.c        -       Py_hexdigits    variable        const char * Py_hexdigits
+Python/sysmodule.c     -       PyId__  variable        _Py_IDENTIFIER(_)
+Modules/_abc.c -       PyId__abc_impl  variable        _Py_IDENTIFIER(_abc_impl)
+Objects/typeobject.c   -       PyId___abstractmethods__        variable        _Py_IDENTIFIER(__abstractmethods__)
+Modules/_abc.c -       PyId___abstractmethods__        variable        _Py_IDENTIFIER(__abstractmethods__)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___aenter__ variable        _Py_IDENTIFIER(__aenter__)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___aexit__  variable        _Py_IDENTIFIER(__aexit__)
+Objects/typeobject.c   slot_am_aiter   PyId___aiter__  variable        _Py_IDENTIFIER(__aiter__)
+Python/ceval.c import_all_from PyId___all__    variable        _Py_IDENTIFIER(__all__)
+Objects/typeobject.c   slot_am_anext   PyId___anext__  variable        _Py_IDENTIFIER(__anext__)
+Python/Python-ast.c    -       PyId_annotation variable        _Py_IDENTIFIER(annotation)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___annotations__    variable        _Py_IDENTIFIER(__annotations__)
+Python/Python-ast.c    -       PyId_arg        variable        _Py_IDENTIFIER(arg)
+Python/Python-ast.c    -       PyId_args       variable        _Py_IDENTIFIER(args)
+Python/Python-ast.c    -       PyId_argtypes   variable        _Py_IDENTIFIER(argtypes)
+Python/Python-ast.c    -       PyId_asname     variable        _Py_IDENTIFIER(asname)
+Python/Python-ast.c    make_type       PyId__ast       variable        _Py_IDENTIFIER(_ast)
+Python/Python-ast.c    -       PyId_attr       variable        _Py_IDENTIFIER(attr)
+Python/Python-ast.c    -       PyId__attributes        variable        _Py_IDENTIFIER(_attributes)
+Objects/typeobject.c   slot_am_await   PyId___await__  variable        _Py_IDENTIFIER(__await__)
+Python/Python-ast.c    -       PyId_bases      variable        _Py_IDENTIFIER(bases)
+Modules/_abc.c -       PyId___bases__  variable        _Py_IDENTIFIER(__bases__)
+Objects/abstract.c     abstract_get_bases      PyId___bases__  variable        _Py_IDENTIFIER(__bases__)
+Objects/typeobject.c   merge_class_dict        PyId___bases__  variable        _Py_IDENTIFIER(__bases__)
+Objects/longobject.c   -       PyId_big        variable        _Py_IDENTIFIER(big)
+Modules/_io/_iomodule.c        _io_open_impl   PyId__blksize   variable        _Py_IDENTIFIER(_blksize)
+Python/Python-ast.c    -       PyId_body       variable        _Py_IDENTIFIER(body)
+Objects/typeobject.c   slot_nb_bool    PyId___bool__   variable        _Py_IDENTIFIER(__bool__)
+Python/sysmodule.c     -       PyId_buffer     variable        _Py_IDENTIFIER(buffer)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___build_class__    variable        _Py_IDENTIFIER(__build_class__)
+Objects/typeobject.c   -       PyId_builtins   variable        _Py_IDENTIFIER(builtins)
+Python/errors.c        -       PyId_builtins   variable        _Py_IDENTIFIER(builtins)
+Python/pythonrun.c     -       PyId_builtins   variable        _Py_IDENTIFIER(builtins)
+Python/sysmodule.c     -       PyId_builtins   variable        _Py_IDENTIFIER(builtins)
+Objects/frameobject.c  -       PyId___builtins__       variable        _Py_IDENTIFIER(__builtins__)
+Python/bltinmodule.c   -       PyId___builtins__       variable        _Py_IDENTIFIER(__builtins__)
+Python/import.c        module_dict_for_exec    PyId___builtins__       variable        _Py_IDENTIFIER(__builtins__)
+Objects/object.c       -       PyId___bytes__  variable        _Py_IDENTIFIER(__bytes__)
+Objects/bytesobject.c  format_obj      PyId___bytes__  variable        _Py_IDENTIFIER(__bytes__)
+Objects/bytesobject.c  bytes_new       PyId___bytes__  variable        _Py_IDENTIFIER(__bytes__)
+Objects/weakrefobject.c        proxy_bytes     PyId___bytes__  variable        _Py_IDENTIFIER(__bytes__)
+Objects/typeobject.c   slot_tp_call    PyId___call__   variable        _Py_IDENTIFIER(__call__)
+Python/Python-ast.c    -       PyId_cause      variable        _Py_IDENTIFIER(cause)
+Objects/typeobject.c   -       PyId___class__  variable        _Py_IDENTIFIER(__class__)
+Modules/_abc.c -       PyId___class__  variable        _Py_IDENTIFIER(__class__)
+Python/compile.c       compiler_enter_scope    PyId___class__  variable        _Py_IDENTIFIER(__class__)
+Objects/abstract.c     recursive_isinstance    PyId___class__  variable        _Py_IDENTIFIER(__class__)
+Objects/typeobject.c   type_new        PyId___classcell__      variable        _Py_IDENTIFIER(__classcell__)
+Objects/typeobject.c   -       PyId___class_getitem__  variable        _Py_IDENTIFIER(__class_getitem__)
+Objects/abstract.c     PyObject_GetItem        PyId___class_getitem__  variable        _Py_IDENTIFIER(__class_getitem__)
+Python/import.c        PyImport_Cleanup        PyId_clear      variable        _Py_IDENTIFIER(clear)
+Python/traceback.c     -       PyId_close      variable        _Py_IDENTIFIER(close)
+Modules/_io/bufferedio.c       -       PyId_close      variable        _Py_IDENTIFIER(close)
+Modules/_io/textio.c   -       PyId_close      variable        _Py_IDENTIFIER(close)
+Objects/genobject.c    gen_close_iter  PyId_close      variable        _Py_IDENTIFIER(close)
+Modules/_dbmmodule.c   dbm__exit__     PyId_close      variable        _Py_IDENTIFIER(close)
+Modules/_gdbmmodule.c  dbm__exit__     PyId_close      variable        _Py_IDENTIFIER(close)
+Python/pythonrun.c     _Py_HandleSystemExit    PyId_code       variable        _Py_IDENTIFIER(code)
+Python/Python-ast.c    -       PyId_col_offset variable        _Py_IDENTIFIER(col_offset)
+Python/Python-ast.c    -       PyId_comparators        variable        _Py_IDENTIFIER(comparators)
+Objects/complexobject.c        try_complex_special_method      PyId___complex__        variable        _Py_IDENTIFIER(__complex__)
+Objects/typeobject.c   slot_sq_contains        PyId___contains__       variable        _Py_IDENTIFIER(__contains__)
+Python/Python-ast.c    -       PyId_context_expr       variable        _Py_IDENTIFIER(context_expr)
+Python/Python-ast.c    -       PyId_conversion variable        _Py_IDENTIFIER(conversion)
+Modules/itertoolsmodule.c      itertools_tee_impl      PyId___copy__   variable        _Py_IDENTIFIER(__copy__)
+Objects/descrobject.c  mappingproxy_copy       PyId_copy       variable        _Py_IDENTIFIER(copy)
+Objects/typeobject.c   import_copyreg  PyId_copyreg    variable        _Py_IDENTIFIER(copyreg)
+Python/Python-ast.c    -       PyId_ctx        variable        _Py_IDENTIFIER(ctx)
+Modules/_io/bufferedio.c       -       PyId__dealloc_warn      variable        _Py_IDENTIFIER(_dealloc_warn)
+Modules/_io/textio.c   -       PyId__dealloc_warn      variable        _Py_IDENTIFIER(_dealloc_warn)
+Modules/_io/textio.c   -       PyId_decode     variable        _Py_IDENTIFIER(decode)
+Python/Python-ast.c    -       PyId_decorator_list     variable        _Py_IDENTIFIER(decorator_list)
+Python/_warnings.c     get_default_action      PyId_defaultaction      variable        _Py_IDENTIFIER(defaultaction)
+Python/Python-ast.c    -       PyId_defaults   variable        _Py_IDENTIFIER(defaults)
+Objects/typeobject.c   slot_tp_finalize        PyId___del__    variable        _Py_IDENTIFIER(__del__)
+Objects/typeobject.c   slot_tp_setattro        PyId___delattr__        variable        _Py_IDENTIFIER(__delattr__)
+Objects/typeobject.c   slot_tp_descr_set       PyId___delete__ variable        _Py_IDENTIFIER(__delete__)
+Objects/typeobject.c   -       PyId___delitem__        variable        _Py_IDENTIFIER(__delitem__)
+Objects/typeobject.c   -       PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Modules/_abc.c -       PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Python/bltinmodule.c   -       PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Python/Python-ast.c    ast_type_reduce PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Python/ceval.c import_all_from PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Objects/bytearrayobject.c      _common_reduce  PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Objects/moduleobject.c module_dir      PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Objects/odictobject.c  odict_reduce    PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Objects/setobject.c    set_reduce      PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Modules/_collectionsmodule.c   deque_reduce    PyId___dict__   variable        _Py_IDENTIFIER(__dict__)
+Objects/dictobject.c   dictviews_sub   PyId_difference_update  variable        _Py_IDENTIFIER(difference_update)
+Python/Python-ast.c    -       PyId_dims       variable        _Py_IDENTIFIER(dims)
+Objects/object.c       -       PyId___dir__    variable        _Py_IDENTIFIER(__dir__)
+Objects/moduleobject.c module_dir      PyId___dir__    variable        _Py_IDENTIFIER(__dir__)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId_displayhook        variable        _Py_IDENTIFIER(displayhook)
+Objects/typeobject.c   -       PyId___doc__    variable        _Py_IDENTIFIER(__doc__)
+Objects/descrobject.c  property_init_impl      PyId___doc__    variable        _Py_IDENTIFIER(__doc__)
+Objects/moduleobject.c module_init_dict        PyId___doc__    variable        _Py_IDENTIFIER(__doc__)
+Objects/moduleobject.c PyModule_SetDocString   PyId___doc__    variable        _Py_IDENTIFIER(__doc__)
+Python/Python-ast.c    -       PyId_elt        variable        _Py_IDENTIFIER(elt)
+Python/Python-ast.c    -       PyId_elts       variable        _Py_IDENTIFIER(elts)
+Modules/faulthandler.c -       PyId_enable     variable        _Py_IDENTIFIER(enable)
+Python/sysmodule.c     -       PyId_encoding   variable        _Py_IDENTIFIER(encoding)
+Python/bltinmodule.c   -       PyId_encoding   variable        _Py_IDENTIFIER(encoding)
+Python/pythonrun.c     PyRun_InteractiveOneObjectEx    PyId_encoding   variable        _Py_IDENTIFIER(encoding)
+Python/Python-ast.c    -       PyId_end_col_offset     variable        _Py_IDENTIFIER(end_col_offset)
+Python/Python-ast.c    -       PyId_end_lineno variable        _Py_IDENTIFIER(end_lineno)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___enter__  variable        _Py_IDENTIFIER(__enter__)
+Objects/typeobject.c   overrides_hash  PyId___eq__     variable        _Py_IDENTIFIER(__eq__)
+Python/bltinmodule.c   -       PyId_errors     variable        _Py_IDENTIFIER(errors)
+Python/Python-ast.c    -       PyId_exc        variable        _Py_IDENTIFIER(exc)
+Python/pythonrun.c     -       PyId_excepthook variable        _Py_IDENTIFIER(excepthook)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___exit__   variable        _Py_IDENTIFIER(__exit__)
+Modules/_pickle.c      do_append       PyId_extend     variable        _Py_IDENTIFIER(extend)
+Python/Python-ast.c    -       PyId__fields    variable        _Py_IDENTIFIER(_fields)
+Objects/moduleobject.c PyModule_GetFilenameObject      PyId___file__   variable        _Py_IDENTIFIER(__file__)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_filename   variable        _Py_IDENTIFIER(filename)
+Python/pythonrun.c     parse_syntax_error      PyId_filename   variable        _Py_IDENTIFIER(filename)
+Modules/_io/textio.c   -       PyId_fileno     variable        _Py_IDENTIFIER(fileno)
+Modules/faulthandler.c -       PyId_fileno     variable        _Py_IDENTIFIER(fileno)
+Python/bltinmodule.c   -       PyId_fileno     variable        _Py_IDENTIFIER(fileno)
+Objects/fileobject.c   PyObject_AsFileDescriptor       PyId_fileno     variable        _Py_IDENTIFIER(fileno)
+Modules/itertoolsmodule.c      zip_longest_new PyId_fillvalue  variable        _Py_IDENTIFIER(fillvalue)
+Python/_warnings.c     get_filter      PyId_filters    variable        _Py_IDENTIFIER(filters)
+Python/Python-ast.c    -       PyId_finalbody  variable        _Py_IDENTIFIER(finalbody)
+Modules/_io/iobase.c   iobase_finalize PyId__finalizing        variable        _Py_IDENTIFIER(_finalizing)
+Python/import.c        import_find_and_load    PyId__find_and_load     variable        _Py_IDENTIFIER(_find_and_load)
+Python/import.c        PyImport_ExecCodeModuleObject   PyId__fix_up_module     variable        _Py_IDENTIFIER(_fix_up_module)
+Python/errors.c        -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Python/pylifecycle.c   -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Python/pythonrun.c     -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Modules/_threadmodule.c        -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Modules/_io/bufferedio.c       -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Modules/_io/textio.c   -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Modules/faulthandler.c -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Python/bltinmodule.c   -       PyId_flush      variable        _Py_IDENTIFIER(flush)
+Objects/abstract.c     PyObject_Format PyId___format__ variable        _Py_IDENTIFIER(__format__)
+Python/Python-ast.c    -       PyId_format_spec        variable        _Py_IDENTIFIER(format_spec)
+Modules/posixmodule.c  path_converter  PyId___fspath__ variable        _Py_IDENTIFIER(__fspath__)
+Modules/posixmodule.c  PyOS_FSPath     PyId___fspath__ variable        _Py_IDENTIFIER(__fspath__)
+Python/Python-ast.c    -       PyId_func       variable        _Py_IDENTIFIER(func)
+Python/Python-ast.c    -       PyId_generators variable        _Py_IDENTIFIER(generators)
+Objects/descrobject.c  mappingproxy_get        PyId_get        variable        _Py_IDENTIFIER(get)
+Modules/_collectionsmodule.c   _count_elements PyId_get        variable        _Py_IDENTIFIER(get)
+Objects/typeobject.c   slot_tp_descr_get       PyId___get__    variable        _Py_IDENTIFIER(__get__)
+Objects/classobject.c  method_reduce   PyId_getattr    variable        _Py_IDENTIFIER(getattr)
+Objects/descrobject.c  descr_reduce    PyId_getattr    variable        _Py_IDENTIFIER(getattr)
+Objects/descrobject.c  wrapper_reduce  PyId_getattr    variable        _Py_IDENTIFIER(getattr)
+Objects/moduleobject.c module_getattro PyId___getattr__        variable        _Py_IDENTIFIER(__getattr__)
+Objects/methodobject.c meth_reduce     PyId_getattr    variable        _Py_IDENTIFIER(getattr)
+Objects/typeobject.c   slot_tp_getattr_hook    PyId___getattr__        variable        _Py_IDENTIFIER(__getattr__)
+Objects/typeobject.c   -       PyId___getattribute__   variable        _Py_IDENTIFIER(__getattribute__)
+Objects/typeobject.c   -       PyId___getitem__        variable        _Py_IDENTIFIER(__getitem__)
+Objects/typeobject.c   _PyObject_GetNewArguments       PyId___getnewargs__     variable        _Py_IDENTIFIER(__getnewargs__)
+Objects/typeobject.c   _PyObject_GetNewArguments       PyId___getnewargs_ex__  variable        _Py_IDENTIFIER(__getnewargs_ex__)
+Modules/_io/textio.c   -       PyId_getpreferredencoding       variable        _Py_IDENTIFIER(getpreferredencoding)
+Python/_warnings.c     get_source_line PyId_get_source variable        _Py_IDENTIFIER(get_source)
+Python/import.c        PyImport_ExecCodeModuleWithPathnames    PyId__get_sourcefile    variable        _Py_IDENTIFIER(_get_sourcefile)
+Objects/typeobject.c   _PyObject_GetState      PyId___getstate__       variable        _Py_IDENTIFIER(__getstate__)
+Python/import.c        PyImport_ImportModuleLevelObject        PyId__handle_fromlist   variable        _Py_IDENTIFIER(_handle_fromlist)
+Python/Python-ast.c    -       PyId_handlers   variable        _Py_IDENTIFIER(handlers)
+Objects/typeobject.c   -       PyId___hash__   variable        _Py_IDENTIFIER(__hash__)
+Python/Python-ast.c    -       PyId_id variable        _Py_IDENTIFIER(id)
+Python/Python-ast.c    -       PyId_ifs        variable        _Py_IDENTIFIER(ifs)
+Python/import.c        PyImport_ReloadModule   PyId_imp        variable        _Py_IDENTIFIER(imp)
+Python/ceval.c import_name     PyId___import__ variable        _Py_IDENTIFIER(__import__)
+Objects/typeobject.c   slot_nb_index   PyId___index__  variable        _Py_IDENTIFIER(__index__)
+Objects/typeobject.c   slot_tp_init    PyId___init__   variable        _Py_IDENTIFIER(__init__)
+Objects/moduleobject.c _PyModuleSpec_IsInitializing    PyId__initializing      variable        _Py_IDENTIFIER(_initializing)
+Objects/typeobject.c   -       PyId___init_subclass__  variable        _Py_IDENTIFIER(__init_subclass__)
+Objects/abstract.c     PyObject_IsInstance     PyId___instancecheck__  variable        _Py_IDENTIFIER(__instancecheck__)
+Objects/dictobject.c   _PyDictView_Intersect   PyId_intersection_update        variable        _Py_IDENTIFIER(intersection_update)
+Modules/_io/iobase.c   -       PyId___IOBase_closed    variable        _Py_IDENTIFIER(__IOBase_closed)
+Objects/typeobject.c   slot_nb_inplace_power   PyId___ipow__   variable        _Py_IDENTIFIER(__ipow__)
+Objects/object.c       -       PyId___isabstractmethod__       variable        _Py_IDENTIFIER(__isabstractmethod__)
+Python/Python-ast.c    -       PyId_is_async   variable        _Py_IDENTIFIER(is_async)
+Modules/_io/bufferedio.c       -       PyId_isatty     variable        _Py_IDENTIFIER(isatty)
+Modules/_io/textio.c   -       PyId_isatty     variable        _Py_IDENTIFIER(isatty)
+Python/pylifecycle.c   create_stdio    PyId_isatty     variable        _Py_IDENTIFIER(isatty)
+Modules/_io/_iomodule.c        _io_open_impl   PyId_isatty     variable        _Py_IDENTIFIER(isatty)
+Python/codecs.c        _PyCodec_LookupTextEncoding     PyId__is_text_encoding  variable        _Py_IDENTIFIER(_is_text_encoding)
+Python/Python-ast.c    -       PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/abstract.c     PyMapping_Items PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/descrobject.c  mappingproxy_items      PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/odictobject.c  odict_reduce    PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/odictobject.c  odict_repr      PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/odictobject.c  mutablemapping_update   PyId_items      variable        _Py_IDENTIFIER(items)
+Objects/typeobject.c   _PyObject_GetItemsIter  PyId_items      variable        _Py_IDENTIFIER(items)
+Modules/_collectionsmodule.c   defdict_reduce  PyId_items      variable        _Py_IDENTIFIER(items)
+Python/Python-ast.c    -       PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/bytearrayobject.c      bytearrayiter_reduce    PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/bytesobject.c  striter_reduce  PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/dictobject.c   dictiter_reduce PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/iterobject.c   iter_reduce     PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/iterobject.c   calliter_reduce PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/listobject.c   listiter_reduce_general PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/odictobject.c  odictiter_reduce        PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/rangeobject.c  rangeiter_reduce        PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/rangeobject.c  longrangeiter_reduce    PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/setobject.c    setiter_reduce  PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/tupleobject.c  tupleiter_reduce        PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/unicodeobject.c        unicodeiter_reduce      PyId_iter       variable        _Py_IDENTIFIER(iter)
+Objects/typeobject.c   slot_tp_iter    PyId___iter__   variable        _Py_IDENTIFIER(__iter__)
+Modules/arraymodule.c  array_arrayiterator___reduce___impl     PyId_iter       variable        _Py_IDENTIFIER(iter)
+Python/Python-ast.c    -       PyId_key        variable        _Py_IDENTIFIER(key)
+Python/Python-ast.c    -       PyId_keys       variable        _Py_IDENTIFIER(keys)
+Objects/abstract.c     PyMapping_Keys  PyId_keys       variable        _Py_IDENTIFIER(keys)
+Objects/descrobject.c  mappingproxy_keys       PyId_keys       variable        _Py_IDENTIFIER(keys)
+Objects/dictobject.c   dict_update_common      PyId_keys       variable        _Py_IDENTIFIER(keys)
+Objects/odictobject.c  mutablemapping_update   PyId_keys       variable        _Py_IDENTIFIER(keys)
+Python/Python-ast.c    -       PyId_keywords   variable        _Py_IDENTIFIER(keywords)
+Python/Python-ast.c    -       PyId_kind       variable        _Py_IDENTIFIER(kind)
+Python/Python-ast.c    -       PyId_kwarg      variable        _Py_IDENTIFIER(kwarg)
+Python/Python-ast.c    -       PyId_kw_defaults        variable        _Py_IDENTIFIER(kw_defaults)
+Python/Python-ast.c    -       PyId_kwonlyargs variable        _Py_IDENTIFIER(kwonlyargs)
+Python/pythonrun.c     -       PyId_last_traceback     variable        _Py_IDENTIFIER(last_traceback)
+Python/pythonrun.c     -       PyId_last_type  variable        _Py_IDENTIFIER(last_type)
+Python/pythonrun.c     -       PyId_last_value variable        _Py_IDENTIFIER(last_value)
+Python/Python-ast.c    -       PyId_left       variable        _Py_IDENTIFIER(left)
+Objects/typeobject.c   -       PyId___len__    variable        _Py_IDENTIFIER(__len__)
+Objects/abstract.c     PyObject_LengthHint     PyId___length_hint__    variable        _Py_IDENTIFIER(__length_hint__)
+Python/Python-ast.c    -       PyId_level      variable        _Py_IDENTIFIER(level)
+Python/Python-ast.c    -       PyId_lineno     variable        _Py_IDENTIFIER(lineno)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_lineno     variable        _Py_IDENTIFIER(lineno)
+Python/pythonrun.c     parse_syntax_error      PyId_lineno     variable        _Py_IDENTIFIER(lineno)
+Objects/longobject.c   -       PyId_little     variable        _Py_IDENTIFIER(little)
+Python/_warnings.c     get_source_line PyId___loader__ variable        _Py_IDENTIFIER(__loader__)
+Objects/moduleobject.c module_init_dict        PyId___loader__ variable        _Py_IDENTIFIER(__loader__)
+Python/import.c        PyImport_ImportModuleLevelObject        PyId__lock_unlock_module        variable        _Py_IDENTIFIER(_lock_unlock_module)
+Python/Python-ast.c    -       PyId_lower      variable        _Py_IDENTIFIER(lower)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId___ltrace__ variable        _Py_IDENTIFIER(__ltrace__)
+Python/pythonrun.c     PyRun_InteractiveOneObjectEx    PyId___main__   variable        _Py_IDENTIFIER(__main__)
+Python/_warnings.c     check_matched   PyId_match      variable        _Py_IDENTIFIER(match)
+Python/bltinmodule.c   -       PyId_metaclass  variable        _Py_IDENTIFIER(metaclass)
+Objects/dictobject.c   dict_subscript  PyId___missing__        variable        _Py_IDENTIFIER(__missing__)
+Modules/_io/bufferedio.c       -       PyId_mode       variable        _Py_IDENTIFIER(mode)
+Modules/_io/textio.c   -       PyId_mode       variable        _Py_IDENTIFIER(mode)
+Python/pylifecycle.c   create_stdio    PyId_mode       variable        _Py_IDENTIFIER(mode)
+Modules/_io/_iomodule.c        _io_open_impl   PyId_mode       variable        _Py_IDENTIFIER(mode)
+Python/Python-ast.c    -       PyId_module     variable        _Py_IDENTIFIER(module)
+Objects/typeobject.c   -       PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Python/Python-ast.c    make_type       PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Python/errors.c        PyErr_NewException      PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Python/errors.c        PyErr_NewException      PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Python/pythonrun.c     print_exception PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Modules/_pickle.c      whichmodule     PyId___module__ variable        _Py_IDENTIFIER(__module__)
+Objects/typeobject.c   type_mro_modified       PyId_mro        variable        _Py_IDENTIFIER(mro)
+Objects/typeobject.c   mro_invoke      PyId_mro        variable        _Py_IDENTIFIER(mro)
+Python/bltinmodule.c   -       PyId___mro_entries__    variable        _Py_IDENTIFIER(__mro_entries__)
+Objects/typeobject.c   type_new        PyId___mro_entries__    variable        _Py_IDENTIFIER(__mro_entries__)
+Python/Python-ast.c    -       PyId_msg        variable        _Py_IDENTIFIER(msg)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_msg        variable        _Py_IDENTIFIER(msg)
+Python/pythonrun.c     parse_syntax_error      PyId_msg        variable        _Py_IDENTIFIER(msg)
+Python/pylifecycle.c   -       PyId_name       variable        _Py_IDENTIFIER(name)
+Modules/_io/fileio.c   -       PyId_name       variable        _Py_IDENTIFIER(name)
+Modules/_io/bufferedio.c       -       PyId_name       variable        _Py_IDENTIFIER(name)
+Modules/_io/textio.c   -       PyId_name       variable        _Py_IDENTIFIER(name)
+Python/Python-ast.c    -       PyId_name       variable        _Py_IDENTIFIER(name)
+Objects/exceptions.c   ImportError_getstate    PyId_name       variable        _Py_IDENTIFIER(name)
+Objects/typeobject.c   -       PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Objects/classobject.c  -       PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/_warnings.c     setup_context   PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/_warnings.c     get_source_line PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/_warnings.c     show_warning    PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/ceval.c import_from     PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/ceval.c import_all_from PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/import.c        resolve_name    PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Objects/moduleobject.c module_init_dict        PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Objects/moduleobject.c PyModule_GetNameObject  PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Objects/moduleobject.c module_getattro PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Objects/weakrefobject.c        weakref_repr    PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Modules/_pickle.c      save_global     PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Modules/_pickle.c      save_reduce     PyId___name__   variable        _Py_IDENTIFIER(__name__)
+Python/Python-ast.c    -       PyId_names      variable        _Py_IDENTIFIER(names)
+Objects/typeobject.c   -       PyId___new__    variable        _Py_IDENTIFIER(__new__)
+Objects/typeobject.c   reduce_newobj   PyId___newobj__ variable        _Py_IDENTIFIER(__newobj__)
+Objects/typeobject.c   reduce_newobj   PyId___newobj_ex__      variable        _Py_IDENTIFIER(__newobj_ex__)
+Objects/typeobject.c   slot_tp_iternext        PyId___next__   variable        _Py_IDENTIFIER(__next__)
+Objects/structseq.c    -       PyId_n_fields   variable        _Py_IDENTIFIER(n_fields)
+Python/ast.c   new_identifier  PyId_NFKC       variable        _Py_IDENTIFIER(NFKC)
+Objects/structseq.c    -       PyId_n_sequence_fields  variable        _Py_IDENTIFIER(n_sequence_fields)
+Objects/structseq.c    -       PyId_n_unnamed_fields   variable        _Py_IDENTIFIER(n_unnamed_fields)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_offset     variable        _Py_IDENTIFIER(offset)
+Python/pythonrun.c     parse_syntax_error      PyId_offset     variable        _Py_IDENTIFIER(offset)
+Python/_warnings.c     get_once_registry       PyId_onceregistry       variable        _Py_IDENTIFIER(onceregistry)
+Python/Python-ast.c    -       PyId_op variable        _Py_IDENTIFIER(op)
+Python/traceback.c     -       PyId_open       variable        _Py_IDENTIFIER(open)
+Python/pylifecycle.c   create_stdio    PyId_open       variable        _Py_IDENTIFIER(open)
+Parser/tokenizer.c     fp_setreadl     PyId_open       variable        _Py_IDENTIFIER(open)
+Objects/fileobject.c   PyFile_FromFd   PyId_open       variable        _Py_IDENTIFIER(open)
+Objects/fileobject.c   PyFile_OpenCodeObject   PyId_open       variable        _Py_IDENTIFIER(open)
+Python/Python-ast.c    -       PyId_operand    variable        _Py_IDENTIFIER(operand)
+Python/Python-ast.c    -       PyId_ops        variable        _Py_IDENTIFIER(ops)
+Python/Python-ast.c    -       PyId_optional_vars      variable        _Py_IDENTIFIER(optional_vars)
+Python/Python-ast.c    -       PyId_orelse     variable        _Py_IDENTIFIER(orelse)
+Python/import.c        resolve_name    PyId___package__        variable        _Py_IDENTIFIER(__package__)
+Objects/moduleobject.c module_init_dict        PyId___package__        variable        _Py_IDENTIFIER(__package__)
+Python/import.c        resolve_name    PyId_parent     variable        _Py_IDENTIFIER(parent)
+Modules/_operator.c    methodcaller_reduce     PyId_partial    variable        _Py_IDENTIFIER(partial)
+Python/sysmodule.c     -       PyId_path       variable        _Py_IDENTIFIER(path)
+Python/traceback.c     -       PyId_path       variable        _Py_IDENTIFIER(path)
+Objects/exceptions.c   ImportError_getstate    PyId_path       variable        _Py_IDENTIFIER(path)
+Modules/main.c pymain_sys_path_add_path0       PyId_path       variable        _Py_IDENTIFIER(path)
+Python/import.c        resolve_name    PyId___path__   variable        _Py_IDENTIFIER(__path__)
+Python/import.c        PyImport_ImportModuleLevelObject        PyId___path__   variable        _Py_IDENTIFIER(__path__)
+Modules/_io/bufferedio.c       -       PyId_peek       variable        _Py_IDENTIFIER(peek)
+Python/Python-ast.c    -       PyId_posonlyargs        variable        _Py_IDENTIFIER(posonlyargs)
+Objects/typeobject.c   slot_nb_power   PyId___pow__    variable        _Py_IDENTIFIER(__pow__)
+Python/bltinmodule.c   -       PyId___prepare__        variable        _Py_IDENTIFIER(__prepare__)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_print_file_and_line        variable        _Py_IDENTIFIER(print_file_and_line)
+Python/pythonrun.c     print_exception PyId_print_file_and_line        variable        _Py_IDENTIFIER(print_file_and_line)
+Python/pythonrun.c     -       PyId_ps1        variable        _Py_IDENTIFIER(ps1)
+Python/pythonrun.c     -       PyId_ps2        variable        _Py_IDENTIFIER(ps2)
+Objects/object.c       -       PyId_Py_Repr    variable        _Py_IDENTIFIER(Py_Repr)
+Objects/classobject.c  -       PyId___qualname__       variable        _Py_IDENTIFIER(__qualname__)
+Objects/descrobject.c  calculate_qualname      PyId___qualname__       variable        _Py_IDENTIFIER(__qualname__)
+Objects/methodobject.c meth_get__qualname__    PyId___qualname__       variable        _Py_IDENTIFIER(__qualname__)
+Objects/typeobject.c   type_new        PyId___qualname__       variable        _Py_IDENTIFIER(__qualname__)
+Modules/_io/textio.c   -       PyId_raw        variable        _Py_IDENTIFIER(raw)
+Python/pylifecycle.c   create_stdio    PyId_raw        variable        _Py_IDENTIFIER(raw)
+Modules/_io/iobase.c   -       PyId_read       variable        _Py_IDENTIFIER(read)
+Modules/_io/bufferedio.c       -       PyId_read       variable        _Py_IDENTIFIER(read)
+Modules/_io/textio.c   -       PyId_read       variable        _Py_IDENTIFIER(read)
+Modules/_io/bufferedio.c       -       PyId_read1      variable        _Py_IDENTIFIER(read1)
+Python/marshal.c       marshal_load    PyId_read       variable        _Py_IDENTIFIER(read)
+Modules/_io/bufferedio.c       -       PyId_readable   variable        _Py_IDENTIFIER(readable)
+Modules/_io/textio.c   -       PyId_readable   variable        _Py_IDENTIFIER(readable)
+Modules/_io/iobase.c   _io__RawIOBase_read_impl        PyId_readall    variable        _Py_IDENTIFIER(readall)
+Modules/_io/bufferedio.c       -       PyId_readinto   variable        _Py_IDENTIFIER(readinto)
+Modules/_io/bufferedio.c       -       PyId_readinto1  variable        _Py_IDENTIFIER(readinto1)
+Python/marshal.c       r_string        PyId_readinto   variable        _Py_IDENTIFIER(readinto)
+Parser/tokenizer.c     fp_setreadl     PyId_readline   variable        _Py_IDENTIFIER(readline)
+Objects/fileobject.c   PyFile_GetLine  PyId_readline   variable        _Py_IDENTIFIER(readline)
+Objects/typeobject.c   object___reduce_ex___impl       PyId___reduce__ variable        _Py_IDENTIFIER(__reduce__)
+Python/import.c        PyImport_ReloadModule   PyId_reload     variable        _Py_IDENTIFIER(reload)
+Modules/_io/textio.c   -       PyId_replace    variable        _Py_IDENTIFIER(replace)
+Python/importdl.c      get_encoded_name        PyId_replace    variable        _Py_IDENTIFIER(replace)
+Objects/typeobject.c   slot_tp_repr    PyId___repr__   variable        _Py_IDENTIFIER(__repr__)
+Modules/_io/textio.c   -       PyId_reset      variable        _Py_IDENTIFIER(reset)
+Python/Python-ast.c    -       PyId_returns    variable        _Py_IDENTIFIER(returns)
+Objects/enumobject.c   reversed_new_impl       PyId___reversed__       variable        _Py_IDENTIFIER(__reversed__)
+Objects/listobject.c   listiter_reduce_general PyId_reversed   variable        _Py_IDENTIFIER(reversed)
+Python/Python-ast.c    -       PyId_right      variable        _Py_IDENTIFIER(right)
+Python/bltinmodule.c   -       PyId___round__  variable        _Py_IDENTIFIER(__round__)
+Modules/_io/textio.c   -       PyId_seek       variable        _Py_IDENTIFIER(seek)
+Modules/_io/iobase.c   _io__IOBase_tell_impl   PyId_seek       variable        _Py_IDENTIFIER(seek)
+Modules/_io/textio.c   -       PyId_seekable   variable        _Py_IDENTIFIER(seekable)
+Python/ceval.c _PyEval_EvalFrameDefault        PyId_send       variable        _Py_IDENTIFIER(send)
+Objects/typeobject.c   slot_tp_descr_set       PyId___set__    variable        _Py_IDENTIFIER(__set__)
+Objects/typeobject.c   slot_tp_setattro        PyId___setattr__        variable        _Py_IDENTIFIER(__setattr__)
+Objects/typeobject.c   -       PyId___setitem__        variable        _Py_IDENTIFIER(__setitem__)
+Modules/_collectionsmodule.c   _count_elements PyId___setitem__        variable        _Py_IDENTIFIER(__setitem__)
+Objects/typeobject.c   -       PyId___set_name__       variable        _Py_IDENTIFIER(__set_name__)
+Modules/_io/textio.c   -       PyId_setstate   variable        _Py_IDENTIFIER(setstate)
+Modules/_pickle.c      load_build      PyId___setstate__       variable        _Py_IDENTIFIER(__setstate__)
+Python/_warnings.c     call_show_warning       PyId__showwarnmsg       variable        _Py_IDENTIFIER(_showwarnmsg)
+Python/pylifecycle.c   wait_for_thread_shutdown        PyId__shutdown  variable        _Py_IDENTIFIER(_shutdown)
+Python/Python-ast.c    -       PyId_simple     variable        _Py_IDENTIFIER(simple)
+Python/sysmodule.c     -       PyId___sizeof__ variable        _Py_IDENTIFIER(__sizeof__)
+Python/Python-ast.c    -       PyId_slice      variable        _Py_IDENTIFIER(slice)
+Objects/typeobject.c   _PyType_GetSlotNames    PyId___slotnames__      variable        _Py_IDENTIFIER(__slotnames__)
+Objects/typeobject.c   _PyType_GetSlotNames    PyId__slotnames variable        _Py_IDENTIFIER(_slotnames)
+Objects/typeobject.c   type_new        PyId___slots__  variable        _Py_IDENTIFIER(__slots__)
+Python/bltinmodule.c   -       PyId_sort       variable        _Py_IDENTIFIER(sort)
+Python/import.c        resolve_name    PyId___spec__   variable        _Py_IDENTIFIER(__spec__)
+Python/import.c        PyImport_ImportModuleLevelObject        PyId___spec__   variable        _Py_IDENTIFIER(__spec__)
+Objects/moduleobject.c module_init_dict        PyId___spec__   variable        _Py_IDENTIFIER(__spec__)
+Objects/moduleobject.c module_getattro PyId___spec__   variable        _Py_IDENTIFIER(__spec__)
+Python/_warnings.c     -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/errors.c        -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/pylifecycle.c   -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/pythonrun.c     -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/sysmodule.c     -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Modules/_threadmodule.c        -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Modules/faulthandler.c -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/bltinmodule.c   -       PyId_stderr     variable        _Py_IDENTIFIER(stderr)
+Python/pylifecycle.c   -       PyId_stdin      variable        _Py_IDENTIFIER(stdin)
+Python/pythonrun.c     -       PyId_stdin      variable        _Py_IDENTIFIER(stdin)
+Python/bltinmodule.c   -       PyId_stdin      variable        _Py_IDENTIFIER(stdin)
+Python/pylifecycle.c   -       PyId_stdout     variable        _Py_IDENTIFIER(stdout)
+Python/pythonrun.c     -       PyId_stdout     variable        _Py_IDENTIFIER(stdout)
+Python/sysmodule.c     -       PyId_stdout     variable        _Py_IDENTIFIER(stdout)
+Python/bltinmodule.c   -       PyId_stdout     variable        _Py_IDENTIFIER(stdout)
+Python/Python-ast.c    -       PyId_step       variable        _Py_IDENTIFIER(step)
+Modules/posixmodule.c  DirEntry_test_mode      PyId_st_mode    variable        _Py_IDENTIFIER(st_mode)
+Modules/_io/textio.c   -       PyId_strict     variable        _Py_IDENTIFIER(strict)
+Python/pythonrun.c     -       PyId_string     variable        _Py_static_string(PyId_string, ""<string>"")
+Modules/timemodule.c   time_strptime   PyId__strptime_time     variable        _Py_IDENTIFIER(_strptime_time)
+Modules/posixmodule.c  wait_helper     PyId_struct_rusage      variable        _Py_IDENTIFIER(struct_rusage)
+Modules/_abc.c -       PyId___subclasscheck__  variable        _Py_IDENTIFIER(__subclasscheck__)
+Objects/abstract.c     PyObject_IsSubclass     PyId___subclasscheck__  variable        _Py_IDENTIFIER(__subclasscheck__)
+Modules/_abc.c -       PyId___subclasshook__   variable        _Py_IDENTIFIER(__subclasshook__)
+Objects/dictobject.c   dictviews_xor   PyId_symmetric_difference_update        variable        _Py_IDENTIFIER(symmetric_difference_update)
+Python/Python-ast.c    -       PyId_tag        variable        _Py_IDENTIFIER(tag)
+Python/Python-ast.c    -       PyId_target     variable        _Py_IDENTIFIER(target)
+Python/Python-ast.c    -       PyId_targets    variable        _Py_IDENTIFIER(targets)
+Modules/_io/textio.c   -       PyId_tell       variable        _Py_IDENTIFIER(tell)
+Python/Python-ast.c    -       PyId_test       variable        _Py_IDENTIFIER(test)
+Python/errors.c        PyErr_SyntaxLocationObject      PyId_text       variable        _Py_IDENTIFIER(text)
+Python/pythonrun.c     parse_syntax_error      PyId_text       variable        _Py_IDENTIFIER(text)
+Python/traceback.c     -       PyId_TextIOWrapper      variable        _Py_IDENTIFIER(TextIOWrapper)
+Python/pylifecycle.c   create_stdio    PyId_TextIOWrapper      variable        _Py_IDENTIFIER(TextIOWrapper)
+Python/pylifecycle.c   -       PyId_threading  variable        _Py_IDENTIFIER(threading)
+Objects/genobject.c    _gen_throw      PyId_throw      variable        _Py_IDENTIFIER(throw)
+Objects/abstract.c     PyNumber_Long   PyId___trunc__  variable        _Py_IDENTIFIER(__trunc__)
+Python/Python-ast.c    -       PyId_type       variable        _Py_IDENTIFIER(type)
+Python/Python-ast.c    -       PyId_type_comment       variable        _Py_IDENTIFIER(type_comment)
+Python/Python-ast.c    -       PyId_type_ignores       variable        _Py_IDENTIFIER(type_ignores)
+Python/errors.c        _PyErr_WriteUnraisableMsg       PyId_unraisablehook     variable        _Py_IDENTIFIER(unraisablehook)
+Objects/dictobject.c   dictviews_or    PyId_update     variable        _Py_IDENTIFIER(update)
+Python/Python-ast.c    -       PyId_upper      variable        _Py_IDENTIFIER(upper)
+Python/Python-ast.c    -       PyId_value      variable        _Py_IDENTIFIER(value)
+Python/Python-ast.c    -       PyId_values     variable        _Py_IDENTIFIER(values)
+Objects/abstract.c     PyMapping_Values        PyId_values     variable        _Py_IDENTIFIER(values)
+Objects/descrobject.c  mappingproxy_values     PyId_values     variable        _Py_IDENTIFIER(values)
+Python/Python-ast.c    -       PyId_vararg     variable        _Py_IDENTIFIER(vararg)
+Python/_warnings.c     already_warned  PyId_version    variable        _Py_IDENTIFIER(version)
+Python/_warnings.c     call_show_warning       PyId_WarningMessage     variable        _Py_IDENTIFIER(WarningMessage)
+Python/_warnings.c     setup_context   PyId___warningregistry__        variable        _Py_IDENTIFIER(__warningregistry__)
+Python/_warnings.c     get_warnings_attr       PyId_warnings   variable        _Py_IDENTIFIER(warnings)
+Python/sysmodule.c     -       PyId_warnoptions        variable        _Py_IDENTIFIER(warnoptions)
+Python/_warnings.c     _PyErr_WarnUnawaitedCoroutine   PyId__warn_unawaited_coroutine  variable        _Py_IDENTIFIER(_warn_unawaited_coroutine)
+Modules/_io/bufferedio.c       -       PyId_writable   variable        _Py_IDENTIFIER(writable)
+Modules/_io/textio.c   -       PyId_writable   variable        _Py_IDENTIFIER(writable)
+Python/sysmodule.c     -       PyId_write      variable        _Py_IDENTIFIER(write)
+Modules/_io/bufferedio.c       -       PyId_write      variable        _Py_IDENTIFIER(write)
+Python/marshal.c       marshal_dump_impl       PyId_write      variable        _Py_IDENTIFIER(write)
+Objects/fileobject.c   PyFile_WriteObject      PyId_write      variable        _Py_IDENTIFIER(write)
+Python/sysmodule.c     -       PyId__xoptions  variable        _Py_IDENTIFIER(_xoptions)
+Python/import.c        _PyImportZip_Init       PyId_zipimporter        variable        _Py_IDENTIFIER(zipimporter)
+Python/initconfig.c    -       Py_IgnoreEnvironmentFlag        variable        int Py_IgnoreEnvironmentFlag
+Python/dynload_shlib.c -       _PyImport_DynLoadFiletab        variable        const char *_PyImport_DynLoadFiletab[]
+Python/frozen.c        -       PyImport_FrozenModules  variable        const struct _frozen * PyImport_FrozenModules
+Modules/config.c       -       _PyImport_Inittab       variable        struct _inittab _PyImport_Inittab[]
+Python/import.c        -       PyImport_Inittab        variable        struct _inittab * PyImport_Inittab
+Modules/_io/textio.c   -       PyIncrementalNewlineDecoder_Type        variable        PyTypeObject PyIncrementalNewlineDecoder_Type
+Python/initconfig.c    -       Py_InspectFlag  variable        int Py_InspectFlag
+Objects/classobject.c  -       PyInstanceMethod_Type   variable        PyTypeObject PyInstanceMethod_Type
+Python/initconfig.c    -       Py_InteractiveFlag      variable        int Py_InteractiveFlag
+Objects/interpreteridobject.c  -       _PyInterpreterID_Type   variable        PyTypeObject _PyInterpreterID_Type
+Modules/_io/iobase.c   -       PyIOBase_Type   variable        PyTypeObject PyIOBase_Type
+Modules/_io/_iomodule.c        -       _PyIO_empty_bytes       variable        PyObject *_PyIO_empty_bytes
+Modules/_io/_iomodule.c        -       _PyIO_empty_str variable        PyObject *_PyIO_empty_str
+Modules/_io/_iomodule.c        -       _PyIO_Module    variable        struct PyModuleDef _PyIO_Module
+Modules/_io/_iomodule.c        -       _PyIO_str_close variable        PyObject *_PyIO_str_close
+Modules/_io/_iomodule.c        -       _PyIO_str_closed        variable        PyObject *_PyIO_str_closed
+Modules/_io/_iomodule.c        -       _PyIO_str_decode        variable        PyObject *_PyIO_str_decode
+Modules/_io/_iomodule.c        -       _PyIO_str_encode        variable        PyObject *_PyIO_str_encode
+Modules/_io/_iomodule.c        -       _PyIO_str_fileno        variable        PyObject *_PyIO_str_fileno
+Modules/_io/_iomodule.c        -       _PyIO_str_flush variable        PyObject *_PyIO_str_flush
+Modules/_io/_iomodule.c        -       _PyIO_str_getstate      variable        PyObject *_PyIO_str_getstate
+Modules/_io/_iomodule.c        -       _PyIO_str_isatty        variable        PyObject *_PyIO_str_isatty
+Modules/_io/_iomodule.c        -       _PyIO_str_newlines      variable        PyObject *_PyIO_str_newlines
+Modules/_io/_iomodule.c        -       _PyIO_str_nl    variable        PyObject *_PyIO_str_nl
+Modules/_io/_iomodule.c        -       _PyIO_str_peek  variable        PyObject *_PyIO_str_peek
+Modules/_io/_iomodule.c        -       _PyIO_str_read  variable        PyObject *_PyIO_str_read
+Modules/_io/_iomodule.c        -       _PyIO_str_read1 variable        PyObject *_PyIO_str_read1
+Modules/_io/_iomodule.c        -       _PyIO_str_readable      variable        PyObject *_PyIO_str_readable
+Modules/_io/_iomodule.c        -       _PyIO_str_readall       variable        PyObject *_PyIO_str_readall
+Modules/_io/_iomodule.c        -       _PyIO_str_readinto      variable        PyObject *_PyIO_str_readinto
+Modules/_io/_iomodule.c        -       _PyIO_str_readline      variable        PyObject *_PyIO_str_readline
+Modules/_io/_iomodule.c        -       _PyIO_str_reset variable        PyObject *_PyIO_str_reset
+Modules/_io/_iomodule.c        -       _PyIO_str_seek  variable        PyObject *_PyIO_str_seek
+Modules/_io/_iomodule.c        -       _PyIO_str_seekable      variable        PyObject *_PyIO_str_seekable
+Modules/_io/_iomodule.c        -       _PyIO_str_setstate      variable        PyObject *_PyIO_str_setstate
+Modules/_io/_iomodule.c        -       _PyIO_str_tell  variable        PyObject *_PyIO_str_tell
+Modules/_io/_iomodule.c        -       _PyIO_str_truncate      variable        PyObject *_PyIO_str_truncate
+Modules/_io/_iomodule.c        -       _PyIO_str_writable      variable        PyObject *_PyIO_str_writable
+Modules/_io/_iomodule.c        -       _PyIO_str_write variable        PyObject *_PyIO_str_write
+Python/initconfig.c    -       Py_IsolatedFlag variable        int Py_IsolatedFlag
+Objects/listobject.c   -       PyListIter_Type variable        PyTypeObject PyListIter_Type
+Objects/listobject.c   -       PyListRevIter_Type      variable        PyTypeObject PyListRevIter_Type
+Objects/listobject.c   -       PyList_Type     variable        PyTypeObject PyList_Type
+Modules/_localemodule.c        -       PyLocale_Methods        variable        static struct PyMethodDef PyLocale_Methods[]
+Objects/longobject.c   -       _PyLong_DigitValue      variable        unsigned char _PyLong_DigitValue[256]
+Objects/longobject.c   -       _PyLong_One     variable        PyObject *_PyLong_One
+Objects/rangeobject.c  -       PyLongRangeIter_Type    variable        PyTypeObject PyLongRangeIter_Type
+Objects/longobject.c   -       PyLong_Type     variable        PyTypeObject PyLong_Type
+Objects/longobject.c   -       _PyLong_Zero    variable        PyObject *_PyLong_Zero
+Objects/memoryobject.c -       _PyManagedBuffer_Type   variable        PyTypeObject _PyManagedBuffer_Type
+Python/bltinmodule.c   -       PyMap_Type      variable        PyTypeObject PyMap_Type
+Objects/obmalloc.c     -       _PyMem  variable        static PyMemAllocatorEx _PyMem
+Objects/descrobject.c  -       PyMemberDescr_Type      variable        PyTypeObject PyMemberDescr_Type
+Objects/obmalloc.c     -       _PyMem_Debug    variable        static struct { debug_alloc_api_t raw; debug_alloc_api_t mem; debug_alloc_api_t obj; } _PyMem_Debug
+Objects/memoryobject.c -       PyMemoryView_Type       variable        PyTypeObject PyMemoryView_Type
+Objects/obmalloc.c     -       _PyMem_Raw      variable        static PyMemAllocatorEx _PyMem_Raw
+Objects/descrobject.c  -       PyMethodDescr_Type      variable        PyTypeObject PyMethodDescr_Type
+Objects/classobject.c  -       PyMethod_Type   variable        PyTypeObject PyMethod_Type
+Objects/descrobject.c  -       _PyMethodWrapper_Type   variable        PyTypeObject _PyMethodWrapper_Type
+Objects/moduleobject.c -       PyModuleDef_Type        variable        PyTypeObject PyModuleDef_Type
+Objects/moduleobject.c -       PyModule_Type   variable        PyTypeObject PyModule_Type
+Objects/namespaceobject.c      -       _PyNamespace_Type       variable        PyTypeObject _PyNamespace_Type
+Objects/object.c       -       _Py_NoneStruct  variable        PyObject _Py_NoneStruct
+Objects/object.c       -       _PyNone_Type    variable        PyTypeObject _PyNone_Type
+Python/initconfig.c    -       Py_NoSiteFlag   variable        int Py_NoSiteFlag
+Objects/object.c       -       _Py_NotImplementedStruct        variable        PyObject _Py_NotImplementedStruct
+Objects/object.c       -       _PyNotImplemented_Type  variable        PyTypeObject _PyNotImplemented_Type
+Python/initconfig.c    -       Py_NoUserSiteDirectory  variable        int Py_NoUserSiteDirectory
+Objects/obmalloc.c     -       _PyObject       variable        static PyMemAllocatorEx _PyObject
+Objects/obmalloc.c     -       _PyObject_Arena variable        static PyObjectArenaAllocator _PyObject_Arena
+Objects/odictobject.c  -       PyODictItems_Type       variable        PyTypeObject PyODictItems_Type
+Objects/odictobject.c  -       PyODictIter_Type        variable        PyTypeObject PyODictIter_Type
+Objects/odictobject.c  -       PyODictKeys_Type        variable        PyTypeObject PyODictKeys_Type
+Objects/odictobject.c  -       PyODict_Type    variable        PyTypeObject PyODict_Type
+Objects/odictobject.c  -       PyODictValues_Type      variable        PyTypeObject PyODictValues_Type
+Python/fileutils.c     -       _Py_open_cloexec_works  variable        int _Py_open_cloexec_works
+Python/initconfig.c    -       Py_OptimizeFlag variable        int Py_OptimizeFlag
+Parser/myreadline.c    -       PyOS_InputHook  variable        int (*PyOS_InputHook)(void)
+Python/pylifecycle.c   -       _PyOS_mystrnicmp_hack   variable        int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t)
+Python/getopt.c        -       _PyOS_optarg    variable        const wchar_t *_PyOS_optarg
+Python/getopt.c        -       _PyOS_opterr    variable        int _PyOS_opterr
+Python/getopt.c        -       _PyOS_optind    variable        Py_ssize_t _PyOS_optind
+Parser/myreadline.c    -       PyOS_ReadlineFunctionPointer    variable        char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *)
+Parser/myreadline.c    -       _PyOS_ReadlineLock      variable        static PyThread_type_lock _PyOS_ReadlineLock
+Parser/myreadline.c    -       _PyOS_ReadlineTState    variable        PyThreadState* _PyOS_ReadlineTState
+Python/modsupport.c    -       _Py_PackageContext      variable        const char *_Py_PackageContext
+Python/graminit.c      -       _PyParser_Grammar       variable        grammar _PyParser_Grammar
+Python/pathconfig.c    -       _Py_path_config variable        _PyPathConfig _Py_path_config
+Objects/picklebufobject.c      -       PyPickleBuffer_Type     variable        PyTypeObject PyPickleBuffer_Type
+Objects/descrobject.c  -       PyProperty_Type variable        PyTypeObject PyProperty_Type
+Python/initconfig.c    -       Py_QuietFlag    variable        int Py_QuietFlag
+Objects/rangeobject.c  -       PyRangeIter_Type        variable        PyTypeObject PyRangeIter_Type
+Objects/rangeobject.c  -       PyRange_Type    variable        PyTypeObject PyRange_Type
+Modules/_io/iobase.c   -       PyRawIOBase_Type        variable        PyTypeObject PyRawIOBase_Type
+Objects/object.c       -       _Py_RefTotal    variable        Py_ssize_t _Py_RefTotal
+Objects/enumobject.c   -       PyReversed_Type variable        PyTypeObject PyReversed_Type
+Python/pylifecycle.c   -       _PyRuntime      variable        _PyRuntimeState _PyRuntime
+Objects/iterobject.c   -       PySeqIter_Type  variable        PyTypeObject PySeqIter_Type
+Objects/setobject.c    -       _PySet_Dummy    variable        PyObject * _PySet_Dummy
+Objects/setobject.c    -       _PySetDummy_Type        variable        static PyTypeObject _PySetDummy_Type
+Objects/setobject.c    -       PySetIter_Type  variable        PyTypeObject PySetIter_Type
+Objects/setobject.c    -       PySet_Type      variable        PyTypeObject PySet_Type
+Objects/sliceobject.c  -       PySlice_Type    variable        PyTypeObject PySlice_Type
+Python/initconfig.c    -       _Py_StandardStreamEncoding      variable        static char *_Py_StandardStreamEncoding
+Python/initconfig.c    -       _Py_StandardStreamErrors        variable        static char *_Py_StandardStreamErrors
+Objects/funcobject.c   -       PyStaticMethod_Type     variable        PyTypeObject PyStaticMethod_Type
+Objects/fileobject.c   -       PyStdPrinter_Type       variable        PyTypeObject PyStdPrinter_Type
+Python/symtable.c      -       PySTEntry_Type  variable        PyTypeObject PySTEntry_Type
+Modules/_io/stringio.c -       PyStringIO_Type variable        PyTypeObject PyStringIO_Type
+Objects/structseq.c    -       PyStructSequence_UnnamedField   variable        char *PyStructSequence_UnnamedField
+Objects/typeobject.c   -       PySuper_Type    variable        PyTypeObject PySuper_Type
+Objects/object.c       -       _Py_SwappedOp   variable        int _Py_SwappedOp[]
+Python/sysmodule.c     -       _PySys_ImplCacheTag     variable        const char *_PySys_ImplCacheTag
+Python/sysmodule.c     -       _PySys_ImplName variable        const char *_PySys_ImplName
+Modules/_io/textio.c   -       PyTextIOBase_Type       variable        PyTypeObject PyTextIOBase_Type
+Modules/_io/textio.c   -       PyTextIOWrapper_Type    variable        PyTypeObject PyTextIOWrapper_Type
+Python/traceback.c     -       PyTraceBack_Type        variable        PyTypeObject PyTraceBack_Type
+Objects/obmalloc.c     -       _Py_tracemalloc_config  variable        struct _PyTraceMalloc_Config _Py_tracemalloc_config
+Objects/boolobject.c   -       _Py_TrueStruct  variable        static struct _longobject _Py_TrueStruct
+Objects/tupleobject.c  -       PyTupleIter_Type        variable        PyTypeObject PyTupleIter_Type
+Objects/tupleobject.c  -       PyTuple_Type    variable        PyTypeObject PyTuple_Type
+Objects/typeobject.c   -       PyType_Type     variable        PyTypeObject PyType_Type
+Python/initconfig.c    -       Py_UnbufferedStdioFlag  variable        int Py_UnbufferedStdioFlag
+Python/pylifecycle.c   -       _Py_UnhandledKeyboardInterrupt  variable        int _Py_UnhandledKeyboardInterrupt
+Objects/unicodeobject.c        -       PyUnicodeIter_Type      variable        PyTypeObject PyUnicodeIter_Type
+Objects/unicodeobject.c        -       PyUnicode_Type  variable        PyTypeObject PyUnicode_Type
+Python/initconfig.c    -       Py_UTF8Mode     variable        int Py_UTF8Mode
+Python/initconfig.c    -       Py_VerboseFlag  variable        int Py_VerboseFlag
+Objects/weakrefobject.c        -       _PyWeakref_CallableProxyType    variable        PyTypeObject _PyWeakref_CallableProxyType
+Objects/weakrefobject.c        -       _PyWeakref_ProxyType    variable        PyTypeObject _PyWeakref_ProxyType
+Objects/weakrefobject.c        -       _PyWeakref_RefType      variable        PyTypeObject _PyWeakref_RefType
+Objects/weakrefobject.c        -       _PyWeakref_RefType      variable        PyTypeObject _PyWeakref_RefType
+Objects/descrobject.c  -       PyWrapperDescr_Type     variable        PyTypeObject PyWrapperDescr_Type
+Python/bltinmodule.c   -       PyZip_Type      variable        PyTypeObject PyZip_Type
+Python/Python-ast.c    -       Raise_fields    variable        static const char *Raise_fields[]
+Python/Python-ast.c    -       Raise_type      variable        static PyTypeObject *Raise_type
+Objects/rangeobject.c  -       range_as_mapping        variable        static PyMappingMethods range_as_mapping
+Objects/rangeobject.c  -       range_as_number variable        static PyNumberMethods range_as_number
+Objects/rangeobject.c  -       range_as_sequence       variable        static PySequenceMethods range_as_sequence
+Objects/rangeobject.c  -       rangeiter_methods       variable        static PyMethodDef rangeiter_methods
+Objects/rangeobject.c  -       range_members   variable        static PyMemberDef range_members[]
+Objects/rangeobject.c  -       range_methods   variable        static PyMethodDef range_methods
+Modules/_io/iobase.c   -       rawiobase_methods       variable        static PyMethodDef rawiobase_methods
+Python/pylifecycle.c   fatal_error     reentrant       variable        static int reentrant
+Modules/faulthandler.c faulthandler_dump_traceback     reentrant       variable        static volatile int reentrant
+Modules/itertoolsmodule.c      -       repeat_methods  variable        static PyMethodDef repeat_methods
+Modules/itertoolsmodule.c      -       repeat_type     variable        static PyTypeObject repeat_type
+Python/Python-ast.c    -       Return_fields   variable        static const char *Return_fields[]
+Python/compile.c       compiler_visit_annotations      return_str      variable        static identifier return_str
+Python/Python-ast.c    -       Return_type     variable        static PyTypeObject *Return_type
+Objects/enumobject.c   -       reversediter_methods    variable        static PyMethodDef reversediter_methods
+Modules/_threadmodule.c        -       rlock_methods   variable        static PyMethodDef rlock_methods
+Modules/_threadmodule.c        -       RLocktype       variable        static PyTypeObject RLocktype
+Objects/typeobject.c   slot_nb_add     rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_subtract        rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_multiply        rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_matrix_multiply rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_remainder       rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_divmod  rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_power_binary    rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_lshift  rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_rshift  rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_and     rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_xor     rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_or      rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_floor_divide    rop_id  variable        _Py_static_string(op_id, OPSTR)
+Objects/typeobject.c   slot_nb_true_divide     rop_id  variable        _Py_static_string(op_id, OPSTR)
+Python/Python-ast.c    -       RShift_singleton        variable        static PyObject *RShift_singleton
+Python/Python-ast.c    -       RShift_type     variable        static PyTypeObject *RShift_type
+Python/pylifecycle.c   -       runtime_initialized     variable        static int runtime_initialized
+Modules/posixmodule.c  -       ScandirIterator_methods variable        static PyMethodDef ScandirIterator_methods
+Modules/posixmodule.c  -       ScandirIteratorType     variable        static PyTypeObject ScandirIteratorType
+Modules/_sre.c -       scanner_members variable        static PyMemberDef scanner_members[]
+Modules/_sre.c -       scanner_methods variable        static PyMethodDef scanner_methods
+Modules/_sre.c -       Scanner_Type    variable        static PyTypeObject Scanner_Type
+Modules/posixmodule.c  -       sched_param_desc        variable        static PyStructSequence_Desc sched_param_desc
+Modules/posixmodule.c  -       sched_param_fields      variable        static PyStructSequence_Field sched_param_fields[]
+Modules/posixmodule.c  -       SchedParamType  variable        static PyTypeObject* SchedParamType
+Objects/iterobject.c   -       seqiter_methods variable        static PyMethodDef seqiter_methods
+Objects/setobject.c    -       set_as_number   variable        static PyNumberMethods set_as_number
+Objects/setobject.c    -       set_as_sequence variable        static PySequenceMethods set_as_sequence
+Python/symtable.c      -       setcomp variable        static identifier setcomp
+Python/Python-ast.c    -       SetComp_fields  variable        static const char *SetComp_fields[]
+Python/Python-ast.c    -       SetComp_type    variable        static PyTypeObject *SetComp_type
+Python/Python-ast.c    -       Set_fields      variable        static const char *Set_fields[]
+Objects/setobject.c    -       setiter_methods variable        static PyMethodDef setiter_methods
+Objects/setobject.c    -       set_methods     variable        static PyMethodDef set_methods
+Python/Python-ast.c    -       Set_type        variable        static PyTypeObject *Set_type
+Modules/signalmodule.c -       SiginfoType     variable        static PyTypeObject SiginfoType
+Modules/signalmodule.c -       signal_methods  variable        static PyMethodDef signal_methods
+Modules/signalmodule.c -       signalmodule    variable        static struct PyModuleDef signalmodule
+Python/import.c        PyImport_Import silly_list      variable        static PyObject *silly_list
+Objects/sliceobject.c  -       slice_cache     variable        static PySliceObject *slice_cache
+Python/Python-ast.c    -       Slice_fields    variable        static const char *Slice_fields[]
+Objects/sliceobject.c  -       slice_members   variable        static PyMemberDef slice_members[]
+Objects/sliceobject.c  -       slice_methods   variable        static PyMethodDef slice_methods
+Python/Python-ast.c    -       slice_type      variable        static PyTypeObject *slice_type
+Python/Python-ast.c    -       Slice_type      variable        static PyTypeObject *Slice_type
+Objects/typeobject.c   -       slotdefs        variable        static slotdef slotdefs[]
+Objects/typeobject.c   -       slotdefs_initialized    variable        static int slotdefs_initialized
+Objects/longobject.c   -       small_ints      variable        static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS]
+Objects/funcobject.c   -       sm_getsetlist   variable        static PyGetSetDef sm_getsetlist[]
+Objects/funcobject.c   -       sm_memberlist   variable        static PyMemberDef sm_memberlist[]
+Modules/xxsubtype.c    -       spamdict_members        variable        static PyMemberDef spamdict_members[]
+Modules/xxsubtype.c    -       spamdict_methods        variable        static PyMethodDef spamdict_methods
+Modules/xxsubtype.c    -       spamdict_type   variable        static PyTypeObject spamdict_type
+Modules/xxsubtype.c    -       spamlist_getsets        variable        static PyGetSetDef spamlist_getsets[]
+Modules/xxsubtype.c    -       spamlist_methods        variable        static PyMethodDef spamlist_methods
+Modules/xxsubtype.c    -       spamlist_type   variable        static PyTypeObject spamlist_type
+Modules/_sre.c -       sremodule       variable        static struct PyModuleDef sremodule
+Modules/faulthandler.c -       stack   variable        static stack_t stack
+Modules/itertoolsmodule.c      -       starmap_methods variable        static PyMethodDef starmap_methods
+Modules/itertoolsmodule.c      -       starmap_type    variable        static PyTypeObject starmap_type
+Python/Python-ast.c    -       Starred_fields  variable        static const char *Starred_fields[]
+Python/Python-ast.c    -       Starred_type    variable        static PyTypeObject *Starred_type
+Python/graminit.c      -       states_0        variable        static state states_0[3]
+Python/graminit.c      -       states_1        variable        static state states_1[2]
+Python/graminit.c      -       states_10       variable        static state states_10[4]
+Python/graminit.c      -       states_11       variable        static state states_11[34]
+Python/graminit.c      -       states_12       variable        static state states_12[2]
+Python/graminit.c      -       states_13       variable        static state states_13[2]
+Python/graminit.c      -       states_14       variable        static state states_14[4]
+Python/graminit.c      -       states_15       variable        static state states_15[2]
+Python/graminit.c      -       states_16       variable        static state states_16[6]
+Python/graminit.c      -       states_17       variable        static state states_17[5]
+Python/graminit.c      -       states_18       variable        static state states_18[3]
+Python/graminit.c      -       states_19       variable        static state states_19[2]
+Python/graminit.c      -       states_2        variable        static state states_2[3]
+Python/graminit.c      -       states_20       variable        static state states_20[3]
+Python/graminit.c      -       states_21       variable        static state states_21[2]
+Python/graminit.c      -       states_22       variable        static state states_22[2]
+Python/graminit.c      -       states_23       variable        static state states_23[2]
+Python/graminit.c      -       states_24       variable        static state states_24[2]
+Python/graminit.c      -       states_25       variable        static state states_25[3]
+Python/graminit.c      -       states_26       variable        static state states_26[2]
+Python/graminit.c      -       states_27       variable        static state states_27[5]
+Python/graminit.c      -       states_28       variable        static state states_28[2]
+Python/graminit.c      -       states_29       variable        static state states_29[3]
+Python/graminit.c      -       states_3        variable        static state states_3[7]
+Python/graminit.c      -       states_30       variable        static state states_30[8]
+Python/graminit.c      -       states_31       variable        static state states_31[4]
+Python/graminit.c      -       states_32       variable        static state states_32[4]
+Python/graminit.c      -       states_33       variable        static state states_33[3]
+Python/graminit.c      -       states_34       variable        static state states_34[2]
+Python/graminit.c      -       states_35       variable        static state states_35[2]
+Python/graminit.c      -       states_36       variable        static state states_36[3]
+Python/graminit.c      -       states_37       variable        static state states_37[3]
+Python/graminit.c      -       states_38       variable        static state states_38[5]
+Python/graminit.c      -       states_39       variable        static state states_39[2]
+Python/graminit.c      -       states_4        variable        static state states_4[2]
+Python/graminit.c      -       states_40       variable        static state states_40[3]
+Python/graminit.c      -       states_41       variable        static state states_41[8]
+Python/graminit.c      -       states_42       variable        static state states_42[8]
+Python/graminit.c      -       states_43       variable        static state states_43[11]
+Python/graminit.c      -       states_44       variable        static state states_44[13]
+Python/graminit.c      -       states_45       variable        static state states_45[6]
+Python/graminit.c      -       states_46       variable        static state states_46[4]
+Python/graminit.c      -       states_47       variable        static state states_47[5]
+Python/graminit.c      -       states_48       variable        static state states_48[5]
+Python/graminit.c      -       states_49       variable        static state states_49[4]
+Python/graminit.c      -       states_5        variable        static state states_5[3]
+Python/graminit.c      -       states_50       variable        static state states_50[6]
+Python/graminit.c      -       states_51       variable        static state states_51[2]
+Python/graminit.c      -       states_52       variable        static state states_52[5]
+Python/graminit.c      -       states_53       variable        static state states_53[5]
+Python/graminit.c      -       states_54       variable        static state states_54[2]
+Python/graminit.c      -       states_55       variable        static state states_55[2]
+Python/graminit.c      -       states_56       variable        static state states_56[3]
+Python/graminit.c      -       states_57       variable        static state states_57[2]
+Python/graminit.c      -       states_58       variable        static state states_58[4]
+Python/graminit.c      -       states_59       variable        static state states_59[3]
+Python/graminit.c      -       states_6        variable        static state states_6[3]
+Python/graminit.c      -       states_60       variable        static state states_60[2]
+Python/graminit.c      -       states_61       variable        static state states_61[2]
+Python/graminit.c      -       states_62       variable        static state states_62[2]
+Python/graminit.c      -       states_63       variable        static state states_63[2]
+Python/graminit.c      -       states_64       variable        static state states_64[2]
+Python/graminit.c      -       states_65       variable        static state states_65[2]
+Python/graminit.c      -       states_66       variable        static state states_66[3]
+Python/graminit.c      -       states_67       variable        static state states_67[4]
+Python/graminit.c      -       states_68       variable        static state states_68[3]
+Python/graminit.c      -       states_69       variable        static state states_69[9]
+Python/graminit.c      -       states_7        variable        static state states_7[9]
+Python/graminit.c      -       states_70       variable        static state states_70[5]
+Python/graminit.c      -       states_71       variable        static state states_71[7]
+Python/graminit.c      -       states_72       variable        static state states_72[3]
+Python/graminit.c      -       states_73       variable        static state states_73[5]
+Python/graminit.c      -       states_74       variable        static state states_74[3]
+Python/graminit.c      -       states_75       variable        static state states_75[3]
+Python/graminit.c      -       states_76       variable        static state states_76[3]
+Python/graminit.c      -       states_77       variable        static state states_77[14]
+Python/graminit.c      -       states_78       variable        static state states_78[8]
+Python/graminit.c      -       states_79       variable        static state states_79[3]
+Python/graminit.c      -       states_8        variable        static state states_8[4]
+Python/graminit.c      -       states_80       variable        static state states_80[4]
+Python/graminit.c      -       states_81       variable        static state states_81[2]
+Python/graminit.c      -       states_82       variable        static state states_82[6]
+Python/graminit.c      -       states_83       variable        static state states_83[3]
+Python/graminit.c      -       states_84       variable        static state states_84[4]
+Python/graminit.c      -       states_85       variable        static state states_85[2]
+Python/graminit.c      -       states_86       variable        static state states_86[3]
+Python/graminit.c      -       states_87       variable        static state states_87[3]
+Python/graminit.c      -       states_88       variable        static state states_88[7]
+Python/graminit.c      -       states_89       variable        static state states_89[3]
+Python/graminit.c      -       states_9        variable        static state states_9[42]
+Python/graminit.c      -       states_90       variable        static state states_90[6]
+Python/graminit.c      -       states_91       variable        static state states_91[11]
+Python/getargs.c       -       static_arg_parsers      variable        static struct _PyArg_Parser *static_arg_parsers
+Objects/unicodeobject.c        -       static_strings  variable        static _Py_Identifier *static_strings
+Modules/_stat.c        -       stat_methods    variable        static PyMethodDef stat_methods
+Modules/_stat.c        -       statmodule      variable        static struct PyModuleDef statmodule
+Modules/posixmodule.c  -       stat_result_desc        variable        static PyStructSequence_Desc stat_result_desc
+Modules/posixmodule.c  -       stat_result_fields      variable        static PyStructSequence_Field stat_result_fields[]
+Modules/posixmodule.c  -       StatResultType  variable        static PyTypeObject* StatResultType
+Modules/posixmodule.c  -       statvfs_result_desc     variable        static PyStructSequence_Desc statvfs_result_desc
+Modules/posixmodule.c  -       statvfs_result_fields   variable        static PyStructSequence_Field statvfs_result_fields[]
+Modules/posixmodule.c  -       StatVFSResultType       variable        static PyTypeObject* StatVFSResultType
+Objects/fileobject.c   -       stdprinter_getsetlist   variable        static PyGetSetDef stdprinter_getsetlist[]
+Objects/fileobject.c   -       stdprinter_methods      variable        static PyMethodDef stdprinter_methods
+Python/symtable.c      -       ste_memberlist  variable        static PyMemberDef ste_memberlist[]
+Python/Python-ast.c    -       stmt_attributes variable        static const char *stmt_attributes[]
+Python/Python-ast.c    -       stmt_type       variable        static PyTypeObject *stmt_type
+Objects/exceptions.c   -       StopIteration_members   variable        static PyMemberDef StopIteration_members[]
+Python/Python-ast.c    -       Store_singleton variable        static PyObject *Store_singleton
+Python/Python-ast.c    -       Store_type      variable        static PyTypeObject *Store_type
+Python/ast_unparse.c   -       _str_close_br   variable        static PyObject *_str_close_br
+Python/ast_unparse.c   -       _str_dbl_close_br       variable        static PyObject *_str_dbl_close_br
+Python/ast_unparse.c   -       _str_dbl_open_br        variable        static PyObject *_str_dbl_open_br
+Modules/_threadmodule.c        -       str_dict        variable        static PyObject *str_dict
+Modules/_io/stringio.c -       stringio_getset variable        static PyGetSetDef stringio_getset[]
+Modules/_io/stringio.c -       stringio_methods        variable        static PyMethodDef stringio_methods
+Objects/unicodeobject.c        -       _string_methods variable        static PyMethodDef _string_methods
+Objects/unicodeobject.c        -       _string_module  variable        static struct PyModuleDef _string_module
+Objects/bytesobject.c  -       striter_methods variable        static PyMethodDef striter_methods
+Python/ast_unparse.c   -       _str_open_br    variable        static PyObject *_str_open_br
+Modules/pwdmodule.c    -       StructPwdType   variable        static PyTypeObject StructPwdType
+Modules/pwdmodule.c    -       struct_pwd_type_desc    variable        static PyStructSequence_Desc struct_pwd_type_desc
+Modules/pwdmodule.c    -       struct_pwd_type_fields  variable        static PyStructSequence_Field struct_pwd_type_fields[]
+Modules/posixmodule.c  wait_helper     struct_rusage   variable        static PyObject *struct_rusage
+Objects/structseq.c    -       structseq_methods       variable        static PyMethodDef structseq_methods
+Modules/posixmodule.c  -       structseq_new   variable        static newfunc structseq_new
+Modules/signalmodule.c -       struct_siginfo_desc     variable        static PyStructSequence_Desc struct_siginfo_desc
+Modules/signalmodule.c -       struct_siginfo_fields   variable        static PyStructSequence_Field struct_siginfo_fields[]
+Modules/timemodule.c   -       StructTimeType  variable        static PyTypeObject StructTimeType
+Modules/timemodule.c   -       struct_time_type_desc   variable        static PyStructSequence_Desc struct_time_type_desc
+Modules/timemodule.c   -       struct_time_type_fields variable        static PyStructSequence_Field struct_time_type_fields[]
+Python/Python-ast.c    -       Subscript_fields        variable        static const char *Subscript_fields[]
+Python/Python-ast.c    -       Subscript_type  variable        static PyTypeObject *Subscript_type
+Python/Python-ast.c    -       Sub_singleton   variable        static PyObject *Sub_singleton
+Python/Python-ast.c    -       Sub_type        variable        static PyTypeObject *Sub_type
+Objects/typeobject.c   -       subtype_getsets_dict_only       variable        static PyGetSetDef subtype_getsets_dict_only[]
+Objects/typeobject.c   -       subtype_getsets_full    variable        static PyGetSetDef subtype_getsets_full[]
+Objects/typeobject.c   -       subtype_getsets_weakref_only    variable        static PyGetSetDef subtype_getsets_weakref_only[]
+Python/Python-ast.c    -       Suite_fields    variable        static const char *Suite_fields[]
+Python/Python-ast.c    -       Suite_type      variable        static PyTypeObject *Suite_type
+Objects/typeobject.c   -       super_members   variable        static PyMemberDef super_members[]
+Modules/symtablemodule.c       -       symtable_methods        variable        static PyMethodDef symtable_methods
+Modules/symtablemodule.c       -       symtablemodule  variable        static struct PyModuleDef symtablemodule
+Objects/exceptions.c   -       SyntaxError_members     variable        static PyMemberDef SyntaxError_members[]
+Python/sysmodule.c     -       sys_methods     variable        static PyMethodDef sys_methods
+Python/sysmodule.c     -       sysmodule       variable        static struct PyModuleDef sysmodule
+Objects/exceptions.c   -       SystemExit_members      variable        static PyMemberDef SystemExit_members[]
+Modules/_tracemalloc.c -       tables_lock     variable        static PyThread_type_lock tables_lock
+Modules/itertoolsmodule.c      -       takewhile_reduce_methods        variable        static PyMethodDef takewhile_reduce_methods
+Modules/itertoolsmodule.c      -       takewhile_type  variable        static PyTypeObject takewhile_type
+Python/pylifecycle.c   -       _TARGET_LOCALES variable        static _LocaleCoercionTarget _TARGET_LOCALES[]
+Python/traceback.c     -       tb_getsetters   variable        static PyGetSetDef tb_getsetters[]
+Python/traceback.c     -       tb_memberlist   variable        static PyMemberDef tb_memberlist[]
+Python/traceback.c     -       tb_methods      variable        static PyMethodDef tb_methods
+Modules/itertoolsmodule.c      -       teedataobject_methods   variable        static PyMethodDef teedataobject_methods
+Modules/itertoolsmodule.c      -       teedataobject_type      variable        static PyTypeObject teedataobject_type
+Modules/itertoolsmodule.c      -       tee_methods     variable        static PyMethodDef tee_methods
+Modules/itertoolsmodule.c      -       tee_type        variable        static PyTypeObject tee_type
+Modules/posixmodule.c  -       TerminalSize_desc       variable        static PyStructSequence_Desc TerminalSize_desc
+Modules/posixmodule.c  -       TerminalSize_fields     variable        static PyStructSequence_Field TerminalSize_fields[]
+Modules/posixmodule.c  -       TerminalSizeType        variable        static PyTypeObject* TerminalSizeType
+Modules/_io/textio.c   -       textiobase_getset       variable        static PyGetSetDef textiobase_getset[]
+Modules/_io/textio.c   -       textiobase_methods      variable        static PyMethodDef textiobase_methods
+Modules/_io/textio.c   -       textiowrapper_getset    variable        static PyGetSetDef textiowrapper_getset[]
+Modules/_io/textio.c   -       textiowrapper_members   variable        static PyMemberDef textiowrapper_members[]
+Modules/_io/textio.c   -       textiowrapper_methods   variable        static PyMethodDef textiowrapper_methods
+Modules/faulthandler.c -       thread  variable        static struct { PyObject *file; int fd; PY_TIMEOUT_T timeout_us; int repeat; PyInterpreterState *interp; int exit; char *header; size_t header_len; PyThread_type_lock cancel_event; PyThread_type_lock running; } thread
+Python/thread.c        -       thread_debug    variable        static int thread_debug
+Modules/_threadmodule.c        -       ThreadError     variable        static PyObject *ThreadError
+Python/thread.c        -       threadinfo_desc variable        static PyStructSequence_Desc threadinfo_desc
+Python/thread.c        -       threadinfo_fields       variable        static PyStructSequence_Field threadinfo_fields[]
+Python/thread.c        -       ThreadInfoType  variable        static PyTypeObject ThreadInfoType
+Modules/_threadmodule.c        -       thread_methods  variable        static PyMethodDef thread_methods
+Modules/_threadmodule.c        -       threadmodule    variable        static struct PyModuleDef threadmodule
+Modules/posixmodule.c  -       ticks_per_second        variable        static long ticks_per_second
+Modules/timemodule.c   _PyTime_GetProcessTimeWithInfo  ticks_per_second        variable        static long ticks_per_second
+Modules/timemodule.c   -       time_methods    variable        static PyMethodDef time_methods
+Modules/timemodule.c   -       timemodule      variable        static struct PyModuleDef timemodule
+Modules/posixmodule.c  -       times_result_desc       variable        static PyStructSequence_Desc times_result_desc
+Modules/posixmodule.c  -       times_result_fields     variable        static PyStructSequence_Field times_result_fields[]
+Modules/posixmodule.c  -       TimesResultType variable        static PyTypeObject* TimesResultType
+Python/context.c       -       _token_missing  variable        static PyObject *_token_missing
+Python/symtable.c      -       top     variable        static identifier top
+Objects/typeobject.c   -       tp_new_methoddef        variable        static struct PyMethodDef tp_new_methoddef[]
+Modules/_tracemalloc.c -       tracemalloc_empty_traceback     variable        static traceback_t tracemalloc_empty_traceback
+Modules/_tracemalloc.c -       tracemalloc_filenames   variable        static _Py_hashtable_t *tracemalloc_filenames
+Modules/_tracemalloc.c -       tracemalloc_peak_traced_memory  variable        static size_t tracemalloc_peak_traced_memory
+Modules/_tracemalloc.c -       tracemalloc_reentrant_key       variable        static Py_tss_t tracemalloc_reentrant_key
+Modules/_tracemalloc.c -       tracemalloc_traceback   variable        static traceback_t *tracemalloc_traceback
+Modules/_tracemalloc.c -       tracemalloc_tracebacks  variable        static _Py_hashtable_t *tracemalloc_tracebacks
+Modules/_tracemalloc.c -       tracemalloc_traced_memory       variable        static size_t tracemalloc_traced_memory
+Modules/_tracemalloc.c -       tracemalloc_traces      variable        static _Py_hashtable_t *tracemalloc_traces
+Objects/boolobject.c   -       true_str        variable        static PyObject *true_str
+Python/Python-ast.c    -       Try_fields      variable        static const char *Try_fields[]
+Python/Python-ast.c    -       Try_type        variable        static PyTypeObject *Try_type
+Objects/tupleobject.c  -       tuple_as_mapping        variable        static PyMappingMethods tuple_as_mapping
+Objects/tupleobject.c  -       tuple_as_sequence       variable        static PySequenceMethods tuple_as_sequence
+Python/Python-ast.c    -       Tuple_fields    variable        static const char *Tuple_fields[]
+Modules/_collectionsmodule.c   -       tuplegetter_members     variable        static PyMemberDef tuplegetter_members[]
+Modules/_collectionsmodule.c   -       tuplegetter_methods     variable        static PyMethodDef tuplegetter_methods
+Modules/_collectionsmodule.c   -       tuplegetter_type        variable        static PyTypeObject tuplegetter_type
+Objects/tupleobject.c  -       tupleiter_methods       variable        static PyMethodDef tupleiter_methods
+Objects/tupleobject.c  -       tuple_methods   variable        static PyMethodDef tuple_methods
+Python/Python-ast.c    -       Tuple_type      variable        static PyTypeObject *Tuple_type
+Objects/typeobject.c   -       type_getsets    variable        static PyGetSetDef type_getsets[]
+Python/Python-ast.c    -       TypeIgnore_fields       variable        static const char *TypeIgnore_fields[]
+Python/Python-ast.c    -       type_ignore_type        variable        static PyTypeObject *type_ignore_type
+Python/Python-ast.c    -       TypeIgnore_type variable        static PyTypeObject *TypeIgnore_type
+Objects/typeobject.c   -       type_members    variable        static PyMemberDef type_members[]
+Objects/typeobject.c   -       type_methods    variable        static PyMethodDef type_methods
+Python/Python-ast.c    -       UAdd_singleton  variable        static PyObject *UAdd_singleton
+Python/Python-ast.c    -       UAdd_type       variable        static PyTypeObject *UAdd_type
+Objects/unicodeobject.c        -       ucnhash_CAPI    variable        static _PyUnicode_Name_CAPI *ucnhash_CAPI
+Python/codecs.c        -       ucnhash_CAPI    variable        static _PyUnicode_Name_CAPI *ucnhash_CAPI
+Python/ast.c   -       u_kind  variable        static PyObject *u_kind
+Modules/posixmodule.c  -       uname_result_desc       variable        static PyStructSequence_Desc uname_result_desc
+Modules/posixmodule.c  -       uname_result_fields     variable        static PyStructSequence_Field uname_result_fields[]
+Modules/posixmodule.c  -       UnameResultType variable        static PyTypeObject* UnameResultType
+Python/Python-ast.c    -       UnaryOp_fields  variable        static const char *UnaryOp_fields[]
+Python/Python-ast.c    -       unaryop_type    variable        static PyTypeObject *unaryop_type
+Python/Python-ast.c    -       UnaryOp_type    variable        static PyTypeObject *UnaryOp_type
+Objects/unicodeobject.c        -       unicode_as_mapping      variable        static PyMappingMethods unicode_as_mapping
+Objects/unicodeobject.c        -       unicode_as_number       variable        static PyNumberMethods unicode_as_number
+Objects/unicodeobject.c        -       unicode_as_sequence     variable        static PySequenceMethods unicode_as_sequence
+Objects/unicodeobject.c        -       unicode_empty   variable        static PyObject *unicode_empty
+Objects/exceptions.c   -       UnicodeError_members    variable        static PyMemberDef UnicodeError_members[]
+Objects/unicodeobject.c        -       unicodeiter_methods     variable        static PyMethodDef unicodeiter_methods
+Objects/unicodeobject.c        -       unicode_latin1  variable        static PyObject *unicode_latin1[256]
+Objects/unicodeobject.c        -       unicode_methods variable        static PyMethodDef unicode_methods
+Modules/_tracemalloc.c -       unknown_filename        variable        static PyObject *unknown_filename
+Python/errors.c        -       UnraisableHookArgs_desc variable        static PyStructSequence_Desc UnraisableHookArgs_desc
+Python/errors.c        -       UnraisableHookArgs_fields       variable        static PyStructSequence_Field UnraisableHookArgs_fields[]
+Python/errors.c        -       UnraisableHookArgsType  variable        static PyTypeObject UnraisableHookArgsType
+Objects/obmalloc.c     -       unused_arena_objects    variable        static struct arena_object* unused_arena_objects
+Python/bootstrap_hash.c        -       urandom_cache   variable        static struct { int fd; dev_t st_dev; ino_t st_ino; } urandom_cache
+Objects/obmalloc.c     -       usable_arenas   variable        static struct arena_object* usable_arenas
+Objects/obmalloc.c     -       usedpools       variable        static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8]
+Modules/faulthandler.c -       user_signals    variable        static user_signal_t *user_signals
+Python/Python-ast.c    -       USub_singleton  variable        static PyObject *USub_singleton
+Python/Python-ast.c    -       USub_type       variable        static PyTypeObject *USub_type
+Python/getversion.c    Py_GetVersion   version variable        static char version[250]
+Python/sysmodule.c     -       version_info_desc       variable        static PyStructSequence_Desc version_info_desc
+Python/sysmodule.c     -       version_info_fields     variable        static PyStructSequence_Field version_info_fields[]
+Python/sysmodule.c     -       VersionInfoType variable        static PyTypeObject VersionInfoType
+Modules/posixmodule.c  -       waitid_result_desc      variable        static PyStructSequence_Desc waitid_result_desc
+Modules/posixmodule.c  -       waitid_result_fields    variable        static PyStructSequence_Field waitid_result_fields[]
+Modules/posixmodule.c  -       WaitidResultType        variable        static PyTypeObject* WaitidResultType
+Modules/signalmodule.c -       wakeup  variable        static volatile struct { SOCKET_T fd; int warn_on_full_buffer; int use_send; } wakeup
+Python/_warnings.c     -       warnings_functions      variable        static PyMethodDef warnings_functions[]
+Python/_warnings.c     -       warningsmodule  variable        static struct PyModuleDef warningsmodule
+Modules/_weakref.c     -       weakref_functions       variable        static PyMethodDef weakref_functions
+Objects/weakrefobject.c        -       weakref_members variable        static PyMemberDef weakref_members[]
+Modules/_weakref.c     -       weakrefmodule   variable        static struct PyModuleDef weakrefmodule
+Python/sysmodule.c     -       whatstrings     variable        static PyObject *whatstrings[8]
+Python/Python-ast.c    -       While_fields    variable        static const char *While_fields[]
+Python/Python-ast.c    -       While_type      variable        static PyTypeObject *While_type
+Python/Python-ast.c    -       With_fields     variable        static const char *With_fields[]
+Python/Python-ast.c    -       withitem_fields variable        static const char *withitem_fields[]
+Python/Python-ast.c    -       withitem_type   variable        static PyTypeObject *withitem_type
+Python/Python-ast.c    -       With_type       variable        static PyTypeObject *With_type
+Objects/descrobject.c  -       wrapperdescr_getset     variable        static PyGetSetDef wrapperdescr_getset[]
+Objects/descrobject.c  -       wrapper_getsets variable        static PyGetSetDef wrapper_getsets[]
+Objects/descrobject.c  -       wrapper_members variable        static PyMemberDef wrapper_members[]
+Objects/descrobject.c  -       wrapper_methods variable        static PyMethodDef wrapper_methods
+Modules/_threadmodule.c        local_new       wr_callback_def variable        static PyMethodDef wr_callback_def
+Modules/xxsubtype.c    -       xxsubtype_functions     variable        static PyMethodDef xxsubtype_functions[]
+Modules/xxsubtype.c    -       xxsubtypemodule variable        static struct PyModuleDef xxsubtypemodule
+Modules/xxsubtype.c    -       xxsubtype_slots variable        static struct PyModuleDef_Slot xxsubtype_slots[]
+Python/Python-ast.c    -       Yield_fields    variable        static const char *Yield_fields[]
+Python/Python-ast.c    -       YieldFrom_fields        variable        static const char *YieldFrom_fields[]
+Python/Python-ast.c    -       YieldFrom_type  variable        static PyTypeObject *YieldFrom_type
+Python/Python-ast.c    -       Yield_type      variable        static PyTypeObject *Yield_type
+Modules/itertoolsmodule.c      -       zip_longest_methods     variable        static PyMethodDef zip_longest_methods
+Modules/itertoolsmodule.c      -       ziplongest_type variable        static PyTypeObject ziplongest_type
+Python/bltinmodule.c   -       zip_methods     variable        static PyMethodDef zip_methods