[Python-checkins] bpo-36876: Add a tool that identifies unsupported global C variables. (#15877)
Eric Snow
webhook-mailer at python.org
Wed Sep 11 14:49:50 EDT 2019
https://github.com/python/cpython/commit/ee536b2020b1f0baad1286dbd4345e13870324af
commit: ee536b2020b1f0baad1286dbd4345e13870324af
branch: master
author: Eric Snow <ericsnowcurrently at gmail.com>
committer: GitHub <noreply at github.com>
date: 2019-09-11T19:49:45+01:00
summary:
bpo-36876: Add a tool that identifies unsupported global C variables. (#15877)
files:
A Lib/test/test_check_c_globals.py
A Lib/test/test_tools/test_c_analyzer/__init__.py
A Lib/test/test_tools/test_c_analyzer/__main__.py
A Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/__init__.py
A Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_files.py
A Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_info.py
A Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_known.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/__init__.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/test___main__.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/test_find.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/test_functional.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/test_show.py
A Lib/test/test_tools/test_c_analyzer/test_c_globals/test_supported.py
A Lib/test/test_tools/test_c_analyzer/test_c_parser/__init__.py
A Lib/test/test_tools/test_c_analyzer/test_c_parser/test_declarations.py
A Lib/test/test_tools/test_c_analyzer/test_c_parser/test_info.py
A Lib/test/test_tools/test_c_analyzer/test_c_parser/test_preprocessor.py
A Lib/test/test_tools/test_c_analyzer/test_c_symbols/__init__.py
A Lib/test/test_tools/test_c_analyzer/test_c_symbols/test_info.py
A Lib/test/test_tools/test_c_analyzer/util.py
A Tools/c-analyzer/README
A Tools/c-analyzer/c-globals.py
A Tools/c-analyzer/c_analyzer_common/__init__.py
A Tools/c-analyzer/c_analyzer_common/_generate.py
A Tools/c-analyzer/c_analyzer_common/files.py
A Tools/c-analyzer/c_analyzer_common/info.py
A Tools/c-analyzer/c_analyzer_common/known.py
A Tools/c-analyzer/c_analyzer_common/util.py
A Tools/c-analyzer/c_globals/README
A Tools/c-analyzer/c_globals/__init__.py
A Tools/c-analyzer/c_globals/__main__.py
A Tools/c-analyzer/c_globals/find.py
A Tools/c-analyzer/c_globals/show.py
A Tools/c-analyzer/c_globals/supported.py
A Tools/c-analyzer/c_parser/__init__.py
A Tools/c-analyzer/c_parser/declarations.py
A Tools/c-analyzer/c_parser/info.py
A Tools/c-analyzer/c_parser/naive.py
A Tools/c-analyzer/c_parser/preprocessor.py
A Tools/c-analyzer/c_parser/source.py
A Tools/c-analyzer/c_symbols/__init__.py
A Tools/c-analyzer/c_symbols/binary.py
A Tools/c-analyzer/c_symbols/info.py
A Tools/c-analyzer/c_symbols/resolve.py
A Tools/c-analyzer/c_symbols/source.py
A Tools/c-analyzer/check-c-globals.py
A Tools/c-analyzer/ignored-globals.txt
A Tools/c-analyzer/ignored.tsv
A Tools/c-analyzer/known.tsv
D Tools/c-globals/README
D Tools/c-globals/check-c-globals.py
D Tools/c-globals/ignored-globals.txt
M Lib/test/test_clinic.py
M Lib/test/test_tools/__init__.py
diff --git a/Lib/test/test_check_c_globals.py b/Lib/test/test_check_c_globals.py
new file mode 100644
index 000000000000..009560e8d98d
--- /dev/null
+++ b/Lib/test/test_check_c_globals.py
@@ -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()
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 244c5fecd349..3d5dc4759d50 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -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 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:
diff --git a/Lib/test/test_tools/__init__.py b/Lib/test/test_tools/__init__.py
index 4d0fca330a11..eb9acad677d5 100644
--- a/Lib/test/test_tools/__init__.py
+++ b/Lib/test/test_tools/__init__.py
@@ -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')
+
+ at 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
index 000000000000..d0b4c045104d
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/__init__.py
@@ -0,0 +1,15 @@
+import contextlib
+import os.path
+import test.test_tools
+from test.support import load_package_tests
+
+
+ at 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
index 000000000000..b5b017de8a8a
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/__main__.py
@@ -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
index 000000000000..e69de29bb2d1
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
index 000000000000..6d14aea78a48
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_files.py
@@ -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
index 000000000000..2d386713b998
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_info.py
@@ -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
index 000000000000..215023da577b
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_analyzer_common/test_known.py
@@ -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
index 000000000000..e69de29bb2d1
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
index 000000000000..5f52c588d7c8
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test___main__.py
@@ -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
index 000000000000..b29f966fd2ad
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_find.py
@@ -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
index 000000000000..927979048448
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_functional.py
@@ -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
index 000000000000..ce1dad85db1b
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_show.py
@@ -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
index 000000000000..1e7d40e2afcb
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_globals/test_supported.py
@@ -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
index 000000000000..e69de29bb2d1
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
index 000000000000..b68744ef0aba
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_declarations.py
@@ -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)
+
+
+ at 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
index 000000000000..1dfe5d066a33
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_info.py
@@ -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
index 000000000000..89e15570d653
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_parser/test_preprocessor.py
@@ -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
index 000000000000..e69de29bb2d1
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
index 000000000000..e029dcf66124
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/test_c_symbols/test_info.py
@@ -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
index 000000000000..ba73b0a4b5fc
--- /dev/null
+++ b/Lib/test/test_tools/test_c_analyzer/util.py
@@ -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-globals/README b/Tools/c-analyzer/README
similarity index 100%
rename from Tools/c-globals/README
rename to Tools/c-analyzer/README
diff --git a/Tools/c-analyzer/c-globals.py b/Tools/c-analyzer/c-globals.py
new file mode 100644
index 000000000000..9afe059b28c6
--- /dev/null
+++ b/Tools/c-analyzer/c-globals.py
@@ -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
index 000000000000..888b16ff41d1
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/__init__.py
@@ -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
index 000000000000..1629aa6b5210
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/_generate.py
@@ -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
index 000000000000..b3cd16c8dc00
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/files.py
@@ -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
index 000000000000..e21738040645
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/info.py
@@ -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
index 000000000000..a0c6dfa5aa47
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/known.py
@@ -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
index 000000000000..511c54f17834
--- /dev/null
+++ b/Tools/c-analyzer/c_analyzer_common/util.py
@@ -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
index 000000000000..772b8be27008
--- /dev/null
+++ b/Tools/c-analyzer/c_globals/README
@@ -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
index 000000000000..e69de29bb2d1
diff --git a/Tools/c-analyzer/c_globals/__main__.py b/Tools/c-analyzer/c_globals/__main__.py
new file mode 100644
index 000000000000..9570fb6a14c4
--- /dev/null
+++ b/Tools/c-analyzer/c_globals/__main__.py
@@ -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
index 000000000000..a51b947cbdf1
--- /dev/null
+++ b/Tools/c-analyzer/c_globals/find.py
@@ -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
index 000000000000..f4298b17b678
--- /dev/null
+++ b/Tools/c-analyzer/c_globals/show.py
@@ -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
index 000000000000..4643e4ef6e88
--- /dev/null
+++ b/Tools/c-analyzer/c_globals/supported.py
@@ -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
index 000000000000..e69de29bb2d1
diff --git a/Tools/c-analyzer/c_parser/declarations.py b/Tools/c-analyzer/c_parser/declarations.py
new file mode 100644
index 000000000000..19fa3ff4e66b
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/declarations.py
@@ -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
index 000000000000..9ab697978638
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/info.py
@@ -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
index 000000000000..e0370cc3d1d4
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/naive.py
@@ -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
index 000000000000..0e2866e4873e
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/preprocessor.py
@@ -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 at echo $* = $($*)\n')
+ tmpfile.write('\t at 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
index 000000000000..f8998c8a338b
--- /dev/null
+++ b/Tools/c-analyzer/c_parser/source.py
@@ -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
index 000000000000..e69de29bb2d1
diff --git a/Tools/c-analyzer/c_symbols/binary.py b/Tools/c-analyzer/c_symbols/binary.py
new file mode 100644
index 000000000000..e125dbd5b5ed
--- /dev/null
+++ b/Tools/c-analyzer/c_symbols/binary.py
@@ -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
index 000000000000..f6ed52c8f071
--- /dev/null
+++ b/Tools/c-analyzer/c_symbols/info.py
@@ -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
index 000000000000..dc876ae0b759
--- /dev/null
+++ b/Tools/c-analyzer/c_symbols/resolve.py
@@ -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
index 000000000000..a7248104c94c
--- /dev/null
+++ b/Tools/c-analyzer/c_symbols/source.py
@@ -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-globals/check-c-globals.py b/Tools/c-analyzer/check-c-globals.py
similarity index 100%
rename from Tools/c-globals/check-c-globals.py
rename to Tools/c-analyzer/check-c-globals.py
diff --git a/Tools/c-globals/ignored-globals.txt b/Tools/c-analyzer/ignored-globals.txt
similarity index 100%
rename from Tools/c-globals/ignored-globals.txt
rename to Tools/c-analyzer/ignored-globals.txt
diff --git a/Tools/c-analyzer/ignored.tsv b/Tools/c-analyzer/ignored.tsv
new file mode 100644
index 000000000000..a0e0e503da6a
--- /dev/null
+++ b/Tools/c-analyzer/ignored.tsv
@@ -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
index 000000000000..ce2afcb9591e
--- /dev/null
+++ b/Tools/c-analyzer/known.tsv
@@ -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
More information about the Python-checkins
mailing list