From 7ec59d8861ef1104c3028678b2cacde4c5693e19 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 24 Dec 2020 11:04:19 -0700 Subject: [PATCH] bpo-36876: [c-analyzer tool] Add a "capi" subcommand to the c-analyzer tool. (gh-23918) This will help identify which C-API items will need to be updated for subinterpreter support. https://bugs.python.org/issue36876 --- Tools/c-analyzer/c_analyzer/__main__.py | 9 +- Tools/c-analyzer/c_common/scriptutil.py | 24 +- Tools/c-analyzer/c_common/tables.py | 176 +++++++ Tools/c-analyzer/c_parser/__main__.py | 3 +- .../c_parser/preprocessor/__main__.py | 5 +- Tools/c-analyzer/check-c-globals.py | 1 + Tools/c-analyzer/cpython/__main__.py | 104 +++- Tools/c-analyzer/cpython/_capi.py | 479 ++++++++++++++++++ Tools/c-analyzer/cpython/_files.py | 69 +++ Tools/c-analyzer/cpython/_parser.py | 36 +- 10 files changed, 849 insertions(+), 57 deletions(-) create mode 100644 Tools/c-analyzer/cpython/_capi.py create mode 100644 Tools/c-analyzer/cpython/_files.py diff --git a/Tools/c-analyzer/c_analyzer/__main__.py b/Tools/c-analyzer/c_analyzer/__main__.py index 44325f2952e..24fc6cd1826 100644 --- a/Tools/c-analyzer/c_analyzer/__main__.py +++ b/Tools/c-analyzer/c_analyzer/__main__.py @@ -263,7 +263,7 @@ FORMATS = { def add_output_cli(parser, *, default='summary'): parser.add_argument('--format', dest='fmt', default=default, choices=tuple(FORMATS)) - def process_args(args): + def process_args(args, *, argv=None): pass return process_args @@ -280,7 +280,7 @@ def _cli_check(parser, checks=None, **kwargs): process_checks = add_checks_cli(parser) elif len(checks) == 1 and type(checks) is not dict and re.match(r'^<.*>$', checks[0]): check = checks[0][1:-1] - def process_checks(args): + def process_checks(args, *, argv=None): args.checks = [check] else: process_checks = add_checks_cli(parser, checks=checks) @@ -428,9 +428,9 @@ def _cli_data(parser, filenames=None, known=None): if known is None: sub.add_argument('--known', required=True) - def process_args(args): + def process_args(args, *, argv): if args.datacmd == 'dump': - process_progress(args) + process_progress(args, argv) return process_args @@ -515,6 +515,7 @@ def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset=None): verbosity, traceback_cm = process_args_by_key( args, + argv, processors[cmd], ['verbosity', 'traceback_cm'], ) diff --git a/Tools/c-analyzer/c_common/scriptutil.py b/Tools/c-analyzer/c_common/scriptutil.py index 50dd7548869..ce69af2b6bd 100644 --- a/Tools/c-analyzer/c_common/scriptutil.py +++ b/Tools/c-analyzer/c_common/scriptutil.py @@ -192,7 +192,7 @@ def add_verbosity_cli(parser): parser.add_argument('-q', '--quiet', action='count', default=0) parser.add_argument('-v', '--verbose', action='count', default=0) - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) key = 'verbosity' if key in ns: @@ -208,7 +208,7 @@ def add_traceback_cli(parser): parser.add_argument('--no-traceback', '--no-tb', dest='traceback', action='store_const', const=False) - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) key = 'traceback_cm' if key in ns: @@ -262,7 +262,7 @@ def add_sepval_cli(parser, opt, dest, choices, *, sep=',', **kwargs): #kwargs.setdefault('metavar', opt.upper()) parser.add_argument(opt, dest=dest, action='append', **kwargs) - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) # XXX Use normalize_selection()? @@ -293,7 +293,7 @@ def add_file_filtering_cli(parser, *, excluded=None): excluded = tuple(excluded or ()) - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) key = 'iter_filenames' if key in ns: @@ -323,7 +323,7 @@ def add_progress_cli(parser, *, threshold=VERBOSITY, **kwargs): parser.add_argument('--no-progress', dest='track_progress', action='store_false') parser.set_defaults(track_progress=True) - def process_args(args): + def process_args(args, *, argv=None): if args.track_progress: ns = vars(args) verbosity = ns.get('verbosity', VERBOSITY) @@ -339,7 +339,7 @@ def add_failure_filtering_cli(parser, pool, *, default=False): metavar=f'"{{all|{"|".join(sorted(pool))}}},..."') parser.add_argument('--no-fail', dest='fail', action='store_const', const=()) - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) fail = ns.pop('fail') @@ -371,7 +371,7 @@ def add_failure_filtering_cli(parser, pool, *, default=False): def add_kind_filtering_cli(parser, *, default=None): parser.add_argument('--kinds', action='append') - def process_args(args): + def process_args(args, *, argv=None): ns = vars(args) kinds = [] @@ -486,18 +486,18 @@ def _flatten_processors(processors): yield from _flatten_processors(proc) -def process_args(args, processors, *, keys=None): +def process_args(args, argv, processors, *, keys=None): processors = _flatten_processors(processors) ns = vars(args) extracted = {} if keys is None: for process_args in processors: - for key in process_args(args): + for key in process_args(args, argv=argv): extracted[key] = ns.pop(key) else: remainder = set(keys) for process_args in processors: - hanging = process_args(args) + hanging = process_args(args, argv=argv) if isinstance(hanging, str): hanging = [hanging] for key in hanging or (): @@ -510,8 +510,8 @@ def process_args(args, processors, *, keys=None): return extracted -def process_args_by_key(args, processors, keys): - extracted = process_args(args, processors, keys=keys) +def process_args_by_key(args, argv, processors, keys): + extracted = process_args(args, argv, processors, keys=keys) return [extracted[key] for key in keys] diff --git a/Tools/c-analyzer/c_common/tables.py b/Tools/c-analyzer/c_common/tables.py index 411152e3f94..85b50192571 100644 --- a/Tools/c-analyzer/c_common/tables.py +++ b/Tools/c-analyzer/c_common/tables.py @@ -1,4 +1,6 @@ import csv +import re +import textwrap from . import NOT_SET, strutil, fsutil @@ -212,3 +214,177 @@ def _normalize_table_file_props(header, sep): else: sep = None return header, sep + + +################################## +# stdout tables + +WIDTH = 20 + + +def resolve_columns(specs): + if isinstance(specs, str): + specs = specs.replace(',', ' ').strip().split() + return _resolve_colspecs(specs) + + +def build_table(specs, *, sep=' ', defaultwidth=None): + columns = resolve_columns(specs) + return _build_table(columns, sep=sep, defaultwidth=defaultwidth) + + +_COLSPEC_RE = re.compile(textwrap.dedent(r''' + ^ + (?: + [[] + ( + (?: [^\s\]] [^\]]* )? + [^\s\]] + ) #