mirror of
				https://github.com/python/cpython.git
				synced 2025-10-26 11:14:33 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			262 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import logging
 | |
| import sys
 | |
| 
 | |
| from c_common.scriptutil import (
 | |
|     add_verbosity_cli,
 | |
|     add_traceback_cli,
 | |
|     add_kind_filtering_cli,
 | |
|     add_files_cli,
 | |
|     add_commands_cli,
 | |
|     process_args_by_key,
 | |
|     configure_logger,
 | |
|     get_prog,
 | |
|     main_for_filenames,
 | |
| )
 | |
| from .preprocessor.__main__ import (
 | |
|     add_common_cli as add_preprocessor_cli,
 | |
| )
 | |
| from .info import KIND
 | |
| from . import parse_file as _iter_parsed
 | |
| 
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| 
 | |
| 
 | |
| def _format_vartype(vartype):
 | |
|     if isinstance(vartype, str):
 | |
|         return vartype
 | |
| 
 | |
|     data = vartype
 | |
|     try:
 | |
|         vartype = data['vartype']
 | |
|     except KeyError:
 | |
|         storage, typequal, typespec, abstract = vartype.values()
 | |
|     else:
 | |
|         storage = data.get('storage')
 | |
|         if storage:
 | |
|             _, typequal, typespec, abstract = vartype.values()
 | |
|         else:
 | |
|             storage, typequal, typespec, abstract = vartype.values()
 | |
| 
 | |
|     vartype = f'{typespec} {abstract}'
 | |
|     if typequal:
 | |
|         vartype = f'{typequal} {vartype}'
 | |
|     if storage:
 | |
|         vartype = f'{storage} {vartype}'
 | |
|     return vartype
 | |
| 
 | |
| 
 | |
| def _get_preprocessor(filename, **kwargs):
 | |
|     return get_processor(filename,
 | |
|                          log_err=print,
 | |
|                          **kwargs
 | |
|                          )
 | |
| 
 | |
| 
 | |
| #######################################
 | |
| # the formats
 | |
| 
 | |
| def fmt_raw(filename, item, *, showfwd=None):
 | |
|     yield str(tuple(item))
 | |
| 
 | |
| 
 | |
| def fmt_summary(filename, item, *, showfwd=None):
 | |
|     if item.filename != filename:
 | |
|         yield f'> {item.filename}'
 | |
| 
 | |
|     if showfwd is None:
 | |
|         LINE = ' {lno:>5} {kind:10} {funcname:40} {fwd:1} {name:40} {data}'
 | |
|     else:
 | |
|         LINE = ' {lno:>5} {kind:10} {funcname:40} {name:40} {data}'
 | |
|     lno = kind = funcname = fwd = name = data = ''
 | |
|     MIN_LINE = len(LINE.format(**locals()))
 | |
| 
 | |
|     fileinfo, kind, funcname, name, data = item
 | |
|     lno = fileinfo.lno if fileinfo and fileinfo.lno >= 0 else ''
 | |
|     funcname = funcname or ' --'
 | |
|     name = name or ' --'
 | |
|     isforward = False
 | |
|     if kind is KIND.FUNCTION:
 | |
|         storage, inline, params, returntype, isforward = data.values()
 | |
|         returntype = _format_vartype(returntype)
 | |
|         data = returntype + params
 | |
|         if inline:
 | |
|             data = f'inline {data}'
 | |
|         if storage:
 | |
|             data = f'{storage} {data}'
 | |
|     elif kind is KIND.VARIABLE:
 | |
|         data = _format_vartype(data)
 | |
|     elif kind is KIND.STRUCT or kind is KIND.UNION:
 | |
|         if data is None:
 | |
|             isforward = True
 | |
|         else:
 | |
|             fields = data
 | |
|             data = f'({len(data)}) {{ '
 | |
|             indent = ',\n' + ' ' * (MIN_LINE + len(data))
 | |
|             data += ', '.join(f.name for f in fields[:5])
 | |
|             fields = fields[5:]
 | |
|             while fields:
 | |
|                 data = f'{data}{indent}{", ".join(f.name for f in fields[:5])}'
 | |
|                 fields = fields[5:]
 | |
|             data += ' }'
 | |
|     elif kind is KIND.ENUM:
 | |
|         if data is None:
 | |
|             isforward = True
 | |
|         else:
 | |
|             names = [d if isinstance(d, str) else d.name
 | |
|                      for d in data]
 | |
|             data = f'({len(data)}) {{ '
 | |
|             indent = ',\n' + ' ' * (MIN_LINE + len(data))
 | |
|             data += ', '.join(names[:5])
 | |
|             names = names[5:]
 | |
|             while names:
 | |
|                 data = f'{data}{indent}{", ".join(names[:5])}'
 | |
|                 names = names[5:]
 | |
|             data += ' }'
 | |
|     elif kind is KIND.TYPEDEF:
 | |
|         data = f'typedef {data}'
 | |
|     elif kind == KIND.STATEMENT:
 | |
|         pass
 | |
|     else:
 | |
|         raise NotImplementedError(item)
 | |
|     if isforward:
 | |
|         fwd = '*'
 | |
|         if not showfwd and showfwd is not None:
 | |
|             return
 | |
|     elif showfwd:
 | |
|         return
 | |
|     kind = kind.value
 | |
|     yield LINE.format(**locals())
 | |
| 
 | |
| 
 | |
| def fmt_full(filename, item, *, showfwd=None):
 | |
|     raise NotImplementedError
 | |
| 
 | |
| 
 | |
| FORMATS = {
 | |
|     'raw': fmt_raw,
 | |
|     'summary': fmt_summary,
 | |
|     'full': fmt_full,
 | |
| }
 | |
| 
 | |
| 
 | |
| def add_output_cli(parser):
 | |
|     parser.add_argument('--format', dest='fmt', default='summary', choices=tuple(FORMATS))
 | |
|     parser.add_argument('--showfwd', action='store_true', default=None)
 | |
|     parser.add_argument('--no-showfwd', dest='showfwd', action='store_false', default=None)
 | |
| 
 | |
|     def process_args(args, *, argv=None):
 | |
|         pass
 | |
|     return process_args
 | |
| 
 | |
| 
 | |
| #######################################
 | |
| # the commands
 | |
| 
 | |
| def _cli_parse(parser, excluded=None, **prepr_kwargs):
 | |
|     process_output = add_output_cli(parser)
 | |
|     process_kinds = add_kind_filtering_cli(parser)
 | |
|     process_preprocessor = add_preprocessor_cli(parser, **prepr_kwargs)
 | |
|     process_files = add_files_cli(parser, excluded=excluded)
 | |
|     return [
 | |
|         process_output,
 | |
|         process_kinds,
 | |
|         process_preprocessor,
 | |
|         process_files,
 | |
|     ]
 | |
| 
 | |
| 
 | |
| def cmd_parse(filenames, *,
 | |
|               fmt='summary',
 | |
|               showfwd=None,
 | |
|               iter_filenames=None,
 | |
|               relroot=None,
 | |
|               **kwargs
 | |
|               ):
 | |
|     if 'get_file_preprocessor' not in kwargs:
 | |
|         kwargs['get_file_preprocessor'] = _get_preprocessor()
 | |
|     try:
 | |
|         do_fmt = FORMATS[fmt]
 | |
|     except KeyError:
 | |
|         raise ValueError(f'unsupported fmt {fmt!r}')
 | |
|     for filename, relfile in main_for_filenames(filenames, iter_filenames, relroot):
 | |
|         for item in _iter_parsed(filename, **kwargs):
 | |
|             item = item.fix_filename(relroot, fixroot=False, normalize=False)
 | |
|             for line in do_fmt(relfile, item, showfwd=showfwd):
 | |
|                 print(line)
 | |
| 
 | |
| 
 | |
| def _cli_data(parser):
 | |
|     ...
 | |
| 
 | |
|     return []
 | |
| 
 | |
| 
 | |
| def cmd_data(filenames,
 | |
|              **kwargs
 | |
|              ):
 | |
|     # XXX
 | |
|     raise NotImplementedError
 | |
| 
 | |
| 
 | |
| COMMANDS = {
 | |
|     'parse': (
 | |
|         'parse the given C source & header files',
 | |
|         [_cli_parse],
 | |
|         cmd_parse,
 | |
|     ),
 | |
|     'data': (
 | |
|         'check/manage local data (e.g. excludes, macros)',
 | |
|         [_cli_data],
 | |
|         cmd_data,
 | |
|     ),
 | |
| }
 | |
| 
 | |
| 
 | |
| #######################################
 | |
| # the script
 | |
| 
 | |
| def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, subset='parse'):
 | |
|     import argparse
 | |
|     parser = argparse.ArgumentParser(
 | |
|         prog=prog or get_prog,
 | |
|     )
 | |
| 
 | |
|     processors = add_commands_cli(
 | |
|         parser,
 | |
|         commands={k: v[1] for k, v in COMMANDS.items()},
 | |
|         commonspecs=[
 | |
|             add_verbosity_cli,
 | |
|             add_traceback_cli,
 | |
|         ],
 | |
|         subset=subset,
 | |
|     )
 | |
| 
 | |
|     args = parser.parse_args(argv)
 | |
|     ns = vars(args)
 | |
| 
 | |
|     cmd = ns.pop('cmd')
 | |
| 
 | |
|     verbosity, traceback_cm = process_args_by_key(
 | |
|         args,
 | |
|         argv,
 | |
|         processors[cmd],
 | |
|         ['verbosity', 'traceback_cm'],
 | |
|     )
 | |
| 
 | |
|     return cmd, ns, verbosity, traceback_cm
 | |
| 
 | |
| 
 | |
| def main(cmd, cmd_kwargs):
 | |
|     try:
 | |
|         run_cmd = COMMANDS[cmd][0]
 | |
|     except KeyError:
 | |
|         raise ValueError(f'unsupported cmd {cmd!r}')
 | |
|     run_cmd(**cmd_kwargs)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     cmd, cmd_kwargs, verbosity, traceback_cm = parse_args()
 | |
|     configure_logger(verbosity)
 | |
|     with traceback_cm:
 | |
|         main(cmd, cmd_kwargs)
 | 
