From a53c6687b65fd91fa4a85a596ba365cb4cfa6467 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Wed, 21 Jun 2023 20:04:01 -0400 Subject: [PATCH] fully type mconf.py --- mesonbuild/coredata.py | 4 +-- mesonbuild/mconf.py | 65 +++++++++++++++++++++++------------------- run_mypy.py | 1 + 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 44b7b51a8..46127c695 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -717,13 +717,13 @@ class CoreData: return dirty - def clear_cache(self): + def clear_cache(self) -> None: self.deps.host.clear() self.deps.build.clear() self.compiler_check_cache.clear() self.run_check_cache.clear() - def get_nondefault_buildtype_args(self): + def get_nondefault_buildtype_args(self) -> T.List[T.Union[T.Tuple[str, str, str], T.Tuple[str, bool, bool]]]: result = [] value = self.options[OptionKey('buildtype')].value if value == 'plain': diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 3f3543875..ddd2ee2d7 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -32,6 +32,9 @@ from .mesonlib import MachineChoice, OptionKey if T.TYPE_CHECKING: import argparse + # cannot be TV_Loggable, because non-ansidecorators do direct string concat + LOGLINE = T.Union[str, mlog.AnsiDecorator] + def add_arguments(parser: 'argparse.ArgumentParser') -> None: coredata.register_builtin_arguments(parser) parser.add_argument('builddir', nargs='?', default='.') @@ -40,7 +43,7 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: parser.add_argument('--no-pager', action='store_false', dest='pager', help='Do not redirect output to a pager') -def stringify(val: T.Any) -> T.Union[str, T.List[T.Any]]: # T.Any because of recursion... +def stringify(val: T.Any) -> str: if isinstance(val, bool): return str(val).lower() elif isinstance(val, list): @@ -57,16 +60,16 @@ class ConfException(mesonlib.MesonException): class Conf: - def __init__(self, build_dir): + def __init__(self, build_dir: str): self.build_dir = os.path.abspath(os.path.realpath(build_dir)) if 'meson.build' in [os.path.basename(self.build_dir), self.build_dir]: self.build_dir = os.path.dirname(self.build_dir) self.build = None self.max_choices_line_length = 60 - self.name_col = [] - self.value_col = [] - self.choices_col = [] - self.descr_col = [] + self.name_col: T.List[LOGLINE] = [] + self.value_col: T.List[LOGLINE] = [] + self.choices_col: T.List[LOGLINE] = [] + self.descr_col: T.List[LOGLINE] = [] self.all_subprojects: T.Set[str] = set() if os.path.isdir(os.path.join(self.build_dir, 'meson-private')): @@ -85,13 +88,13 @@ class Conf: else: raise ConfException(f'Directory {build_dir} is neither a Meson build directory nor a project source directory.') - def clear_cache(self): + def clear_cache(self) -> None: self.coredata.clear_cache() - def set_options(self, options) -> bool: + def set_options(self, options: T.Dict[OptionKey, str]) -> bool: return self.coredata.set_options(options) - def save(self): + def save(self) -> None: # Do nothing when using introspection if self.default_values_only: return @@ -127,12 +130,16 @@ class Conf: mlog.log(line[0]) continue - def wrap_text(text, width): + def wrap_text(text: LOGLINE, width: int) -> mlog.TV_LoggableList: raw = text.text if isinstance(text, mlog.AnsiDecorator) else text indent = ' ' if raw.startswith('[') else '' - wrapped = textwrap.wrap(raw, width, subsequent_indent=indent) + wrapped_ = textwrap.wrap(raw, width, subsequent_indent=indent) + # We cast this because https://github.com/python/mypy/issues/1965 + # mlog.TV_LoggableList does not provide __len__ for stringprotocol if isinstance(text, mlog.AnsiDecorator): - wrapped = [mlog.AnsiDecorator(i, text.code) for i in wrapped] + wrapped = T.cast('T.List[LOGLINE]', [mlog.AnsiDecorator(i, text.code) for i in wrapped_]) + else: + wrapped = T.cast('T.List[LOGLINE]', wrapped_) # Add padding here to get even rows, as `textwrap.wrap()` will # only shorten, not lengthen each item return [str(i) + ' ' * (width - len(i)) for i in wrapped] @@ -149,15 +156,15 @@ class Conf: items = [l[i] if l[i] else ' ' * four_column[i] for i in range(4)] mlog.log(*items) - def split_options_per_subproject(self, options: 'coredata.KeyedOptionDictType') -> T.Dict[str, 'coredata.KeyedOptionDictType']: - result: T.Dict[str, 'coredata.KeyedOptionDictType'] = {} + def split_options_per_subproject(self, options: 'coredata.KeyedOptionDictType') -> T.Dict[str, 'coredata.MutableKeyedOptionDictType']: + result: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = {} for k, o in options.items(): if k.subproject: self.all_subprojects.add(k.subproject) result.setdefault(k.subproject, {})[k] = o return result - def _add_line(self, name, value, choices, descr) -> None: + def _add_line(self, name: LOGLINE, value: LOGLINE, choices: LOGLINE, descr: LOGLINE) -> None: if isinstance(name, mlog.AnsiDecorator): name.text = ' ' * self.print_margin + name.text else: @@ -167,21 +174,21 @@ class Conf: self.choices_col.append(choices) self.descr_col.append(descr) - def add_option(self, name, descr, value, choices): + def add_option(self, name: str, descr: str, value: T.Any, choices: T.Any) -> None: value = stringify(value) choices = stringify(choices) self._add_line(mlog.green(name), mlog.yellow(value), mlog.blue(choices), descr) - def add_title(self, title): - title = mlog.cyan(title) + def add_title(self, title: str) -> None: + newtitle = mlog.cyan(title) descr = mlog.cyan('Description') value = mlog.cyan('Default Value' if self.default_values_only else 'Current Value') choices = mlog.cyan('Possible Values') self._add_line('', '', '', '') - self._add_line(title, value, choices, descr) - self._add_line('-' * len(title), '-' * len(value), '-' * len(choices), '-' * len(descr)) + self._add_line(newtitle, value, choices, descr) + self._add_line('-' * len(newtitle), '-' * len(value), '-' * len(choices), '-' * len(descr)) - def add_section(self, section): + def add_section(self, section: str) -> None: self.print_margin = 0 self._add_line('', '', '', '') self._add_line(mlog.normal_yellow(section + ':'), '', '', '') @@ -202,11 +209,11 @@ class Conf: printable_value = auto.printable_value() self.add_option(str(root), o.description, printable_value, o.choices) - def print_conf(self, pager: bool): + def print_conf(self, pager: bool) -> None: if pager: mlog.start_pager() - def print_default_values_warning(): + def print_default_values_warning() -> None: mlog.warning('The source directory instead of the build directory was specified.') mlog.warning('Only the default values for the project are printed.') @@ -223,10 +230,10 @@ class Conf: test_option_names = {OptionKey('errorlogs'), OptionKey('stdsplit')} - dir_options: 'coredata.KeyedOptionDictType' = {} - test_options: 'coredata.KeyedOptionDictType' = {} - core_options: 'coredata.KeyedOptionDictType' = {} - module_options: T.Dict[str, 'coredata.KeyedOptionDictType'] = collections.defaultdict(dict) + dir_options: 'coredata.MutableKeyedOptionDictType' = {} + test_options: 'coredata.MutableKeyedOptionDictType' = {} + core_options: 'coredata.MutableKeyedOptionDictType' = {} + module_options: T.Dict[str, 'coredata.MutableKeyedOptionDictType'] = collections.defaultdict(dict) for k, v in self.coredata.options.items(): if k in dir_option_names: dir_options[k] = v @@ -285,7 +292,7 @@ class Conf: self.print_nondefault_buildtype_options() - def print_nondefault_buildtype_options(self): + def print_nondefault_buildtype_options(self) -> None: mismatching = self.coredata.get_nondefault_buildtype_args() if not mismatching: return @@ -294,7 +301,7 @@ class Conf: for m in mismatching: mlog.log(f'{m[0]:21}{m[1]:10}{m[2]:10}') -def run(options): +def run(options: argparse.Namespace) -> int: coredata.parse_cmd_line_options(options) builddir = os.path.abspath(os.path.realpath(options.builddir)) print_only = not options.cmd_line_options and not options.clearcache diff --git a/run_mypy.py b/run_mypy.py index 6920ff843..3be79bca4 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -37,6 +37,7 @@ modules = [ 'mesonbuild/utils/core.py', 'mesonbuild/utils/platform.py', 'mesonbuild/utils/universal.py', + 'mesonbuild/mconf.py', 'mesonbuild/mdist.py', 'mesonbuild/minit.py', 'mesonbuild/minstall.py',