aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/mlog.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/mlog.py')
-rw-r--r--mesonbuild/mlog.py124
1 files changed, 70 insertions, 54 deletions
diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py
index 0434274..e8ee6c8 100644
--- a/mesonbuild/mlog.py
+++ b/mesonbuild/mlog.py
@@ -18,13 +18,15 @@ import sys
import time
import platform
from contextlib import contextmanager
+import typing
"""This is (mostly) a standalone module used to write logging
information about Meson runs. Some output goes to screen,
some to logging dir and some goes to both."""
-def _windows_ansi():
- from ctypes import windll, byref
+def _windows_ansi() -> bool:
+ # windll only exists on windows, so mypy will get mad
+ from ctypes import windll, byref # type: ignore
from ctypes.wintypes import DWORD
kernel = windll.kernel32
@@ -35,48 +37,48 @@ def _windows_ansi():
# ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0x4
# If the call to enable VT processing fails (returns 0), we fallback to
# original behavior
- return kernel.SetConsoleMode(stdout, mode.value | 0x4) or os.environ.get('ANSICON')
+ return bool(kernel.SetConsoleMode(stdout, mode.value | 0x4) or os.environ.get('ANSICON'))
if platform.system().lower() == 'windows':
- colorize_console = os.isatty(sys.stdout.fileno()) and _windows_ansi()
+ colorize_console = os.isatty(sys.stdout.fileno()) and _windows_ansi() # type: bool
else:
colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('TERM') != 'dumb'
-log_dir = None
-log_file = None
-log_fname = 'meson-log.txt'
-log_depth = 0
-log_timestamp_start = None
-log_fatal_warnings = False
-log_disable_stdout = False
-log_errors_only = False
-
-def disable():
+log_dir = None # type: typing.Optional[str]
+log_file = None # type: typing.Optional[typing.TextIO]
+log_fname = 'meson-log.txt' # type: str
+log_depth = 0 # type: int
+log_timestamp_start = None # type: typing.Optional[float]
+log_fatal_warnings = False # type: bool
+log_disable_stdout = False # type: bool
+log_errors_only = False # type: bool
+
+def disable() -> None:
global log_disable_stdout
log_disable_stdout = True
-def enable():
+def enable() -> None:
global log_disable_stdout
log_disable_stdout = False
-def set_quiet():
+def set_quiet() -> None:
global log_errors_only
log_errors_only = True
-def set_verbose():
+def set_verbose() -> None:
global log_errors_only
log_errors_only = False
-def initialize(logdir, fatal_warnings=False):
+def initialize(logdir: str, fatal_warnings: bool = False) -> None:
global log_dir, log_file, log_fatal_warnings
log_dir = logdir
log_file = open(os.path.join(logdir, log_fname), 'w', encoding='utf8')
log_fatal_warnings = fatal_warnings
-def set_timestamp_start(start):
+def set_timestamp_start(start: float) -> None:
global log_timestamp_start
log_timestamp_start = start
-def shutdown():
+def shutdown() -> typing.Optional[str]:
global log_file
if log_file is not None:
path = log_file.name
@@ -89,12 +91,12 @@ def shutdown():
class AnsiDecorator:
plain_code = "\033[0m"
- def __init__(self, text, code, quoted=False):
+ def __init__(self, text: str, code: str, quoted: bool = False):
self.text = text
self.code = code
self.quoted = quoted
- def get_text(self, with_codes):
+ def get_text(self, with_codes: bool) -> str:
text = self.text
if with_codes:
text = self.code + self.text + AnsiDecorator.plain_code
@@ -102,26 +104,28 @@ class AnsiDecorator:
text = '"{}"'.format(text)
return text
-def bold(text, quoted=False):
+def bold(text: str, quoted: bool = False) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1m", quoted=quoted)
-def red(text):
+def red(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;31m")
-def green(text):
+def green(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;32m")
-def yellow(text):
+def yellow(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;33m")
-def blue(text):
+def blue(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;34m")
-def cyan(text):
+def cyan(text: str) -> AnsiDecorator:
return AnsiDecorator(text, "\033[1;36m")
-def process_markup(args, keep):
- arr = []
+# This really should be AnsiDecorator or anything that implements
+# __str__(), but that requires protocols from typing_extensions
+def process_markup(args: typing.Sequence[typing.Union[AnsiDecorator, str]], keep: bool) -> typing.List[str]:
+ arr = [] # type: typing.List[str]
if log_timestamp_start is not None:
arr = ['[{:.3f}]'.format(time.monotonic() - log_timestamp_start)]
for arg in args:
@@ -135,7 +139,7 @@ def process_markup(args, keep):
arr.append(str(arg))
return arr
-def force_print(*args, **kwargs):
+def force_print(*args: str, **kwargs: typing.Any) -> None:
global log_disable_stdout
if log_disable_stdout:
return
@@ -155,41 +159,51 @@ def force_print(*args, **kwargs):
cleaned = raw.encode('ascii', 'replace').decode('ascii')
print(cleaned, end='')
-def debug(*args, **kwargs):
+# We really want a heterogenous dict for this, but that's in typing_extensions
+def debug(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
arr = process_markup(args, False)
if log_file is not None:
- print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+ print(*arr, file=log_file, **kwargs)
log_file.flush()
-def log(*args, is_error=False, **kwargs):
+def log(*args: typing.Union[str, AnsiDecorator], is_error: bool = False,
+ **kwargs: typing.Any) -> None:
global log_errors_only
arr = process_markup(args, False)
if log_file is not None:
- print(*arr, file=log_file, **kwargs) # Log file never gets ANSI codes.
+ print(*arr, file=log_file, **kwargs)
log_file.flush()
if colorize_console:
arr = process_markup(args, True)
if not log_errors_only or is_error:
force_print(*arr, **kwargs)
-def _log_error(severity, *args, **kwargs):
+def _log_error(severity: str, *rargs: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
from .mesonlib import get_error_location_string
from .environment import build_filename
from .mesonlib import MesonException
+
+ # The tping requirements here are non-obvious. Lists are invariant,
+ # therefore List[A] and List[Union[A, B]] are not able to be joined
if severity == 'warning':
- args = (yellow('WARNING:'),) + args
+ label = [yellow('WARNING:')] # type: typing.List[typing.Union[str, AnsiDecorator]]
elif severity == 'error':
- args = (red('ERROR:'),) + args
+ label = [red('ERROR:')]
elif severity == 'deprecation':
- args = (red('DEPRECATION:'),) + args
+ label = [red('DEPRECATION:')]
else:
- assert False, 'Invalid severity ' + severity
+ raise MesonException('Invalid severity ' + severity)
+ # rargs is a tuple, not a list
+ args = label + list(rargs)
location = kwargs.pop('location', None)
if location is not None:
location_file = os.path.join(location.subdir, build_filename)
location_str = get_error_location_string(location_file, location.lineno)
- args = (location_str,) + args
+ # Unions are frankly awful, and we have to cast here to get mypy
+ # to understand that the list concatenation is safe
+ location_list = typing.cast(typing.List[typing.Union[str, AnsiDecorator]], [location_str])
+ args = location_list + args
log(*args, **kwargs)
@@ -197,40 +211,42 @@ def _log_error(severity, *args, **kwargs):
if log_fatal_warnings:
raise MesonException("Fatal warnings enabled, aborting")
-def error(*args, **kwargs):
+def error(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('error', *args, **kwargs, is_error=True)
-def warning(*args, **kwargs):
+def warning(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('warning', *args, **kwargs, is_error=True)
-def deprecation(*args, **kwargs):
+def deprecation(*args: typing.Union[str, AnsiDecorator], **kwargs: typing.Any) -> None:
return _log_error('deprecation', *args, **kwargs, is_error=True)
-def exception(e, prefix=red('ERROR:')):
+def exception(e: Exception, prefix: AnsiDecorator = red('ERROR:')) -> None:
log()
- args = []
+ args = [] # type: typing.List[typing.Union[AnsiDecorator, str]]
if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'):
- args.append('%s:%d:%d:' % (e.file, e.lineno, e.colno))
+ # Mypy can't figure this out, and it's pretty easy to vidual inspect
+ # that this is correct, so we'll just ignore it.
+ args.append('%s:%d:%d:' % (e.file, e.lineno, e.colno)) # type: ignore
if prefix:
args.append(prefix)
- args.append(e)
+ args.append(str(e))
log(*args)
# Format a list for logging purposes as a string. It separates
# all but the last item with commas, and the last with 'and'.
-def format_list(list):
- l = len(list)
+def format_list(list_: typing.List[str]) -> str:
+ l = len(list_)
if l > 2:
- return ' and '.join([', '.join(list[:-1]), list[-1]])
+ return ' and '.join([', '.join(list_[:-1]), list_[-1]])
elif l == 2:
- return ' and '.join(list)
+ return ' and '.join(list_)
elif l == 1:
- return list[0]
+ return list_[0]
else:
return ''
@contextmanager
-def nested():
+def nested() -> typing.Generator[None, None, None]:
global log_depth
log_depth += 1
try: