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.
pull/13609/head
Dylan Baker 3 months ago
parent 8b5e53b654
commit ca5490f021
  1. 28
      mesonbuild/machinefile.py
  2. 3
      run_mypy.py

@ -12,8 +12,12 @@ from . import mparser
from .mesonlib import MesonException from .mesonlib import MesonException
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from typing_extensions import TypeAlias
from .coredata import StrOrBytesPath from .coredata import StrOrBytesPath
SectionT: TypeAlias = T.Union[str, int, bool, T.List[str], T.List['SectionT']]
class CmdLineFileParser(configparser.ConfigParser): class CmdLineFileParser(configparser.ConfigParser):
def __init__(self) -> None: def __init__(self) -> None:
@ -32,8 +36,8 @@ class CmdLineFileParser(configparser.ConfigParser):
class MachineFileParser(): class MachineFileParser():
def __init__(self, filenames: T.List[str], sourcedir: str) -> None: def __init__(self, filenames: T.List[str], sourcedir: str) -> None:
self.parser = CmdLineFileParser() self.parser = CmdLineFileParser()
self.constants: T.Dict[str, T.Union[str, bool, int, T.List[str]]] = {'True': True, 'False': False} self.constants: T.Dict[str, SectionT] = {'True': True, 'False': False}
self.sections: T.Dict[str, T.Dict[str, T.Union[str, bool, int, T.List[str]]]] = {} self.sections: T.Dict[str, T.Dict[str, SectionT]] = {}
for fname in filenames: for fname in filenames:
try: try:
@ -58,9 +62,9 @@ class MachineFileParser():
continue continue
self.sections[s] = self._parse_section(s) 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() 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): for entry, value in self.parser.items(s):
if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry: if ' ' in entry or '\t' in entry or "'" in entry or '"' in entry:
raise MesonException(f'Malformed variable name {entry!r} in machine file.') raise MesonException(f'Malformed variable name {entry!r} in machine file.')
@ -79,7 +83,7 @@ class MachineFileParser():
self.scope[entry] = res self.scope[entry] = res
return section 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)): if isinstance(node, (mparser.StringNode)):
return node.value return node.value
elif isinstance(node, mparser.BooleanNode): elif isinstance(node, mparser.BooleanNode):
@ -89,7 +93,6 @@ class MachineFileParser():
elif isinstance(node, mparser.ParenthesizedNode): elif isinstance(node, mparser.ParenthesizedNode):
return self._evaluate_statement(node.inner) return self._evaluate_statement(node.inner)
elif isinstance(node, mparser.ArrayNode): 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] return [self._evaluate_statement(arg) for arg in node.args.arguments]
elif isinstance(node, mparser.IdNode): elif isinstance(node, mparser.IdNode):
return self.scope[node.value] return self.scope[node.value]
@ -97,20 +100,21 @@ class MachineFileParser():
l = self._evaluate_statement(node.left) l = self._evaluate_statement(node.left)
r = self._evaluate_statement(node.right) r = self._evaluate_statement(node.right)
if node.operation == 'add': if node.operation == 'add':
if (isinstance(l, str) and isinstance(r, str)) or \ if isinstance(l, str) and isinstance(r, str):
(isinstance(l, list) and isinstance(r, list)): return l + r
if isinstance(l, list) and isinstance(r, list):
return l + r return l + r
elif node.operation == 'div': elif node.operation == 'div':
if isinstance(l, str) and isinstance(r, str): if isinstance(l, str) and isinstance(r, str):
return os.path.join(l, r) return os.path.join(l, r)
raise MesonException('Unsupported node type') 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) parser = MachineFileParser(filenames, sourcedir)
return parser.sections return parser.sections
class MachineFileStore: class MachineFileStore:
def __init__(self, native_files, cross_files, source_dir): def __init__(self, native_files: T.Optional[T.List[str]], cross_files: T.Optional[T.List[str]], source_dir: str):
self.native = MachineFileParser(native_files if native_files is not None else [], source_dir).sections self.native = parse_machine_files(native_files if native_files is not None else [], source_dir)
self.cross = MachineFileParser(cross_files if cross_files is not None else [], source_dir).sections self.cross = parse_machine_files(cross_files if cross_files is not None else [], source_dir)

@ -1,4 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# Copyright © 2024 Intel Corporation
from pathlib import Path from pathlib import Path
import argparse import argparse
@ -38,6 +40,7 @@ modules = [
'mesonbuild/interpreter/mesonmain.py', 'mesonbuild/interpreter/mesonmain.py',
'mesonbuild/interpreter/interpreterobjects.py', 'mesonbuild/interpreter/interpreterobjects.py',
'mesonbuild/interpreter/type_checking.py', 'mesonbuild/interpreter/type_checking.py',
'mesonbuild/machinefile.py',
'mesonbuild/mcompile.py', 'mesonbuild/mcompile.py',
'mesonbuild/mdevenv.py', 'mesonbuild/mdevenv.py',
'mesonbuild/utils/core.py', 'mesonbuild/utils/core.py',

Loading…
Cancel
Save