interpreter: use typed_* args for the summary function

This also includes a few type annotation cleans for the Summary object.

Getting the positional arguments exactly right is impossible, as this is
really a function with two different signatures:
```
summary(value: dictionary): void
summary(key: string, value: any): void
```
We can get close enough in the typed_pos_args by enforcing that there
are two parameters, one required and one optional, and that the first
must be either a dictionary or a string.
pull/9531/head
Dylan Baker 3 years ago
parent 3295621706
commit d05a0fbf33
  1. 45
      mesonbuild/interpreter/interpreter.py
  2. 7
      mesonbuild/interpreter/kwargs.py

@ -121,19 +121,14 @@ def stringifyUserArguments(args, quote=False):
raise InvalidArguments('Function accepts only strings, integers, bools, lists, dictionaries and lists thereof.') raise InvalidArguments('Function accepts only strings, integers, bools, lists, dictionaries and lists thereof.')
class Summary: class Summary:
def __init__(self, project_name, project_version): def __init__(self, project_name: str, project_version: str):
self.project_name = project_name self.project_name = project_name
self.project_version = project_version self.project_version = project_version
self.sections = collections.defaultdict(dict) self.sections = collections.defaultdict(dict)
self.max_key_len = 0 self.max_key_len = 0
def add_section(self, section, values, kwargs, subproject): def add_section(self, section: str, values: T.Dict[str, T.Any], bool_yn: bool,
bool_yn = kwargs.get('bool_yn', False) list_sep: T.Optional[str], subproject: str) -> None:
if not isinstance(bool_yn, bool):
raise InterpreterException('bool_yn keyword argument must be boolean')
list_sep = kwargs.get('list_sep')
if list_sep is not None and not isinstance(list_sep, str):
raise InterpreterException('list_sep keyword argument must be string')
for k, v in values.items(): for k, v in values.items():
if k in self.sections[section]: if k in self.sections[section]:
raise InterpreterException(f'Summary section {section!r} already have key {k!r}') raise InterpreterException(f'Summary section {section!r} already have key {k!r}')
@ -267,7 +262,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.environment = self.build.environment self.environment = self.build.environment
self.coredata = self.environment.get_coredata() self.coredata = self.environment.get_coredata()
self.backend = backend self.backend = backend
self.summary = {} self.summary: T.Dict[str, 'Summary'] = {}
self.modules = {} self.modules = {}
# Subproject directory is usually the name of the subproject, but can # Subproject directory is usually the name of the subproject, but can
# be different for dependencies provided by wrap files. # be different for dependencies provided by wrap files.
@ -1193,31 +1188,33 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log(mlog.bold('Message:'), *args) mlog.log(mlog.bold('Message:'), *args)
@noArgsFlattening @noArgsFlattening
@FeatureNewKwargs('summary', '0.54.0', ['list_sep'])
@permittedKwargs({'section', 'bool_yn', 'list_sep'})
@FeatureNew('summary', '0.53.0') @FeatureNew('summary', '0.53.0')
def func_summary(self, node, args, kwargs): @typed_pos_args('summary', (str, dict), optargs=[object])
if len(args) == 1: @typed_kwargs(
'summary',
KwargInfo('section', str, default=''),
KwargInfo('bool_yn', bool, default=False),
KwargInfo('list_sep', (str, NoneType), since='0.54.0')
)
def func_summary(self, node: mparser.BaseNode, args: T.Tuple[T.Union[str, T.Dict[str, T.Any]], T.Optional[T.Any]],
kwargs: 'kwargs.Summary') -> None:
if args[1] is None:
if not isinstance(args[0], dict): if not isinstance(args[0], dict):
raise InterpreterException('Summary first argument must be dictionary.') raise InterpreterException('Summary first argument must be dictionary.')
values = args[0] values = args[0]
elif len(args) == 2: else:
if not isinstance(args[0], str): if not isinstance(args[0], str):
raise InterpreterException('Summary first argument must be string.') raise InterpreterException('Summary first argument must be string.')
values = {args[0]: args[1]} values = {args[0]: args[1]}
else: self.summary_impl(kwargs['section'], values, kwargs)
raise InterpreterException('Summary accepts at most 2 arguments.')
section = kwargs.get('section', '')
if not isinstance(section, str):
raise InterpreterException('Summary\'s section keyword argument must be string.')
self.summary_impl(section, values, kwargs)
def summary_impl(self, section, values, kwargs): def summary_impl(self, section: str, values, kwargs: 'kwargs.Summary') -> None:
if self.subproject not in self.summary: if self.subproject not in self.summary:
self.summary[self.subproject] = Summary(self.active_projectname, self.project_version) self.summary[self.subproject] = Summary(self.active_projectname, self.project_version)
self.summary[self.subproject].add_section(section, values, kwargs, self.subproject) self.summary[self.subproject].add_section(
section, values, kwargs['bool_yn'], kwargs['list_sep'], self.subproject)
def _print_summary(self): def _print_summary(self) -> None:
# Add automatic 'Supbrojects' section in main project. # Add automatic 'Supbrojects' section in main project.
all_subprojects = collections.OrderedDict() all_subprojects = collections.OrderedDict()
for name, subp in sorted(self.subprojects.items()): for name, subp in sorted(self.subprojects.items()):
@ -1244,7 +1241,7 @@ external dependencies (including libraries) must go to "dependencies".''')
sorted_options = sorted(self.user_defined_options.cmd_line_options.items()) sorted_options = sorted(self.user_defined_options.cmd_line_options.items())
values.update({str(k): v for k, v in sorted_options}) values.update({str(k): v for k, v in sorted_options})
if values: if values:
self.summary_impl('User defined options', values, {}) self.summary_impl('User defined options', values, {'bool_yn': False, 'list_sep': None})
# Print all summaries, main project last. # Print all summaries, main project last.
mlog.log('') # newline mlog.log('') # newline
main_summary = self.summary.pop('', None) main_summary = self.summary.pop('', None)

@ -219,3 +219,10 @@ class _FoundProto(Protocol):
class Subdir(TypedDict): class Subdir(TypedDict):
if_found: T.List[_FoundProto] if_found: T.List[_FoundProto]
class Summary(TypedDict):
section: str
bool_yn: bool
list_sep: T.Optional[str]

Loading…
Cancel
Save