From fc41fd3542f1bafc0b8a2ade1ca92b25c072e054 Mon Sep 17 00:00:00 2001 From: Eli Schwartz Date: Sun, 15 Oct 2023 17:57:45 -0400 Subject: [PATCH] add type annotations to mesonmain An oddity: sys.stdout is statically defined as type TextIO instead of TextIOWrapper, and thus doesn't have a .reconfigure method. This is because they expect people to override sys.stdout with other objects, which we do not do. Instead, assume it is always correct. There are two final errors due to metaprogramming: ``` mesonbuild/mesonmain.py:196:13: error: Returning Any from function declared to return "int" [no-any-return] mesonbuild/mesonmain.py:225:9: error: Returning Any from function declared to return "int" [no-any-return] ``` --- mesonbuild/mesonmain.py | 38 ++++++++++++++++++++------------------ mesonbuild/rewriter.py | 3 ++- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 0fbc717f8..c01be49e5 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -15,11 +15,12 @@ import os.path import platform import importlib import argparse +import typing as T from .utils.core import MesonException, MesonBugException from . import mlog -def errorhandler(e, command): +def errorhandler(e: Exception, command: str) -> int: import traceback if isinstance(e, MesonException): mlog.exception(e) @@ -43,8 +44,8 @@ def errorhandler(e, command): if command == 'runpython': return 2 elif isinstance(e, OSError): - mlog.exception("Unhandled python OSError. This is probably not a Meson bug, " - "but an issue with your build environment.") + mlog.exception(Exception("Unhandled python OSError. This is probably not a Meson bug, " + "but an issue with your build environment.")) return e.errno else: # Exception msg = 'Unhandled python exception' @@ -58,7 +59,7 @@ def errorhandler(e, command): # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ class CommandLineParser: - def __init__(self): + def __init__(self) -> None: # only import these once we do full argparse processing from . import mconf, mdist, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata, mcompile, mdevenv from .scripts import env2mfile @@ -68,8 +69,8 @@ class CommandLineParser: self.term_width = shutil.get_terminal_size().columns self.formatter = lambda prog: argparse.HelpFormatter(prog, max_help_position=int(self.term_width / 2), width=self.term_width) - self.commands = {} - self.hidden_commands = [] + self.commands: T.Dict[str, argparse.ArgumentParser] = {} + self.hidden_commands: T.List[str] = [] self.parser = argparse.ArgumentParser(prog='meson', formatter_class=self.formatter) self.subparsers = self.parser.add_subparsers(title='Commands', dest='command', description='If no command is specified it defaults to setup command.') @@ -109,7 +110,8 @@ class CommandLineParser: self.add_command('unstable-coredata', munstable_coredata.add_arguments, munstable_coredata.run, help_msg=argparse.SUPPRESS) - def add_command(self, name, add_arguments_func, run_func, help_msg, aliases=None): + def add_command(self, name: str, add_arguments_func: T.Callable[[argparse.ArgumentParser], None], + run_func: T.Callable[[argparse.Namespace], int], help_msg: str, aliases: T.List[str] = None) -> None: aliases = aliases or [] # FIXME: Cannot have hidden subparser: # https://bugs.python.org/issue22848 @@ -123,13 +125,13 @@ class CommandLineParser: for i in [name] + aliases: self.commands[i] = p - def add_runpython_arguments(self, parser: argparse.ArgumentParser): + def add_runpython_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument('-c', action='store_true', dest='eval_arg', default=False) parser.add_argument('--version', action='version', version=platform.python_version()) parser.add_argument('script_file') parser.add_argument('script_args', nargs=argparse.REMAINDER) - def run_runpython_command(self, options): + def run_runpython_command(self, options: argparse.Namespace) -> int: sys.argv[1:] = options.script_args if options.eval_arg: exec(options.script_file) @@ -139,17 +141,17 @@ class CommandLineParser: runpy.run_path(options.script_file, run_name='__main__') return 0 - def add_help_arguments(self, parser): + def add_help_arguments(self, parser: argparse.ArgumentParser) -> None: parser.add_argument('command', nargs='?', choices=list(self.commands.keys())) - def run_help_command(self, options): + def run_help_command(self, options: argparse.Namespace) -> int: if options.command: self.commands[options.command].print_help() else: self.parser.print_help() return 0 - def run(self, args): + def run(self, args: T.List[str]) -> int: implicit_setup_command_notice = False # If first arg is not a known command, assume user wants to run the setup # command. @@ -193,7 +195,7 @@ class CommandLineParser: 'Meson will require Python 3.7 or newer', fatal=False) mlog.shutdown() -def run_script_command(script_name, script_args): +def run_script_command(script_name: str, script_args: T.List[str]) -> int: # Map script name to module name for those that doesn't match script_map = {'exe': 'meson_exe', 'install': 'meson_install', @@ -216,16 +218,16 @@ def run_script_command(script_name, script_args): mlog.exception(e) return 1 -def ensure_stdout_accepts_unicode(): +def ensure_stdout_accepts_unicode() -> None: if sys.stdout.encoding and not sys.stdout.encoding.upper().startswith('UTF-'): - sys.stdout.reconfigure(errors='surrogateescape') + sys.stdout.reconfigure(errors='surrogateescape') # type: ignore[attr-defined] -def set_meson_command(mainfile): +def set_meson_command(mainfile: str) -> None: # Set the meson command that will be used to run scripts and so on from . import mesonlib mesonlib.set_meson_command(mainfile) -def run(original_args, mainfile): +def run(original_args: T.List[str], mainfile: str) -> int: if os.environ.get('MESON_SHOW_DEPRECATIONS'): # workaround for https://bugs.python.org/issue34624 import warnings @@ -274,7 +276,7 @@ def run(original_args, mainfile): set_meson_command(mainfile) return CommandLineParser().run(args) -def main(): +def main() -> int: # Always resolve the command path so Ninja can find it for regen, tests, etc. if 'meson.exe' in sys.executable: assert os.path.isabs(sys.executable) diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index 680eed1f8..434b21974 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -18,6 +18,7 @@ import json, os, re, sys import typing as T if T.TYPE_CHECKING: + from argparse import ArgumentParser, HelpFormatter from .mparser import BaseNode class RewriterException(MesonException): @@ -25,7 +26,7 @@ class RewriterException(MesonException): # Note: when adding arguments, please also add them to the completion # scripts in $MESONSRC/data/shell-completions/ -def add_arguments(parser, formatter): +def add_arguments(parser: ArgumentParser, formatter: T.Callable[[str], HelpFormatter]) -> None: parser.add_argument('-s', '--sourcedir', type=str, default='.', metavar='SRCDIR', help='Path to source directory.') parser.add_argument('-V', '--verbose', action='store_true', default=False, help='Enable verbose output') parser.add_argument('-S', '--skip-errors', dest='skip', action='store_true', default=False, help='Skip errors instead of aborting')