From c9955588bf26c37f908e73dbdfc375363e05e0f6 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Tue, 3 Sep 2024 15:46:03 -0700 Subject: [PATCH] machinefile: Make fully typesafe With the use of `typing_extensions.TypeAlias` we can get recursive types, which solves most of the issues we had with this file. --- mesonbuild/machinefile.py | 28 ++++++++++++++++------------ run_mypy.py | 3 +++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/mesonbuild/machinefile.py b/mesonbuild/machinefile.py index 690e1e62f82c..a3aeae522713 100644 --- a/mesonbuild/machinefile.py +++ b/mesonbuild/machinefile.py @@ -12,8 +12,12 @@ from .mesonlib import MesonException if T.TYPE_CHECKING: + from typing_extensions import TypeAlias + from .coredata import StrOrBytesPath + SectionT: TypeAlias = T.Union[str, int, bool, T.List[str], T.List['SectionT']] + class CmdLineFileParser(configparser.ConfigParser): def __init__(self) -> None: @@ -32,8 +36,8 @@ def optionxform(self, optionstr: str) -> str: class MachineFileParser(): def __init__(self, filenames: T.List[str], sourcedir: str) -> None: self.parser = CmdLineFileParser() - self.constants: T.Dict[str, T.Union[str, bool, int, T.List[str]]] = {'True': True, 'False': False} - self.sections: T.Dict[str, T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = {} + self.constants: T.Dict[str, SectionT] = {'True': True, 'False': False} + self.sections: T.Dict[str, T.Dict[str, SectionT]] = {} for fname in filenames: try: @@ -58,9 +62,9 @@ def __init__(self, filenames: T.List[str], sourcedir: str) -> None: continue self.sections[s] = self._parse_section(s) - def _parse_section(self, s: str) -> T.Dict[str, T.Union[str, bool, int, T.List[str]]]: + def _parse_section(self, s: str) -> T.Dict[str, SectionT]: self.scope = self.constants.copy() - section: T.Dict[str, T.Union[str, bool, int, T.List[str]]] = {} + section: T.Dict[str, SectionT] = {} for entry, value in self.parser.items(s): if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry: raise MesonException(f'Malformed variable name {entry!r} in machine file.') @@ -79,7 +83,7 @@ def _parse_section(self, s: str) -> T.Dict[str, T.Union[str, bool, int, T.List[s self.scope[entry] = res return section - def _evaluate_statement(self, node: mparser.BaseNode) -> T.Union[str, bool, int, T.List[str]]: + def _evaluate_statement(self, node: mparser.BaseNode) -> SectionT: if isinstance(node, (mparser.StringNode)): return node.value elif isinstance(node, mparser.BooleanNode): @@ -89,7 +93,6 @@ def _evaluate_statement(self, node: mparser.BaseNode) -> T.Union[str, bool, int, elif isinstance(node, mparser.ParenthesizedNode): return self._evaluate_statement(node.inner) elif isinstance(node, mparser.ArrayNode): - # TODO: This is where recursive types would come in handy return [self._evaluate_statement(arg) for arg in node.args.arguments] elif isinstance(node, mparser.IdNode): return self.scope[node.value] @@ -97,20 +100,21 @@ def _evaluate_statement(self, node: mparser.BaseNode) -> T.Union[str, bool, int, l = self._evaluate_statement(node.left) r = self._evaluate_statement(node.right) if node.operation == 'add': - if (isinstance(l, str) and isinstance(r, str)) or \ - (isinstance(l, list) and isinstance(r, list)): + if isinstance(l, str) and isinstance(r, str): + return l + r + if isinstance(l, list) and isinstance(r, list): return l + r elif node.operation == 'div': if isinstance(l, str) and isinstance(r, str): return os.path.join(l, r) raise MesonException('Unsupported node type') -def parse_machine_files(filenames: T.List[str], sourcedir: str): +def parse_machine_files(filenames: T.List[str], sourcedir: str) -> T.Dict[str, T.Dict[str, SectionT]]: parser = MachineFileParser(filenames, sourcedir) return parser.sections class MachineFileStore: - def __init__(self, native_files, cross_files, source_dir): - self.native = MachineFileParser(native_files if native_files is not None else [], source_dir).sections - self.cross = MachineFileParser(cross_files if cross_files is not None else [], source_dir).sections + def __init__(self, native_files: T.Optional[T.List[str]], cross_files: T.Optional[T.List[str]], source_dir: str): + self.native = parse_machine_files(native_files if native_files is not None else [], source_dir) + self.cross = parse_machine_files(cross_files if cross_files is not None else [], source_dir) diff --git a/run_mypy.py b/run_mypy.py index f72e96b3d3a2..7ed9720c0b22 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# Copyright © 2024 Intel Corporation from pathlib import Path import argparse @@ -38,6 +40,7 @@ 'mesonbuild/interpreter/mesonmain.py', 'mesonbuild/interpreter/interpreterobjects.py', 'mesonbuild/interpreter/type_checking.py', + 'mesonbuild/machinefile.py', 'mesonbuild/mcompile.py', 'mesonbuild/mdevenv.py', 'mesonbuild/utils/core.py',