mlog: Remove using of `**kwargs: T.Any`

This is annoying because we can't get proper auto-completion of mlog,
and because ultimately it was allowing keyword arguments to be silently
dropped on the floor. This does make the code a little more verbose, but
I think the trade-offs of completion + better safety are worth it.

PEP692, which will be part of python 3.12, provides a more elegant
solution using `TypedDicts` to annotate `**kwargs`, which we should
consider in the future.
pull/11179/head
Dylan Baker 3 years ago committed by Eli Schwartz
parent 76bead7e15
commit d9dc5a7f74
  1. 88
      mesonbuild/mlog.py

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import os
import io
import sys
@ -27,6 +28,9 @@ from pathlib import Path
if T.TYPE_CHECKING:
from ._typing import StringProtocol, SizedStringProtocol
from .mparser import BaseNode
"""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."""
@ -218,12 +222,12 @@ def process_markup(args: T.Sequence[TV_Loggable], keep: bool) -> T.List[str]:
arr.append(str(arg))
return arr
def force_print(*args: str, nested: bool, **kwargs: T.Any) -> None:
def force_print(*args: str, nested: bool, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
if log_disable_stdout:
return
iostr = io.StringIO()
kwargs['file'] = iostr
print(*args, **kwargs)
print(*args, sep=sep, end=end, file=iostr)
raw = iostr.getvalue()
if log_depth:
@ -242,11 +246,11 @@ def force_print(*args: str, nested: bool, **kwargs: T.Any) -> None:
cleaned = raw.encode('ascii', 'replace').decode('ascii')
print(cleaned, end='')
# We really want a heterogeneous dict for this, but that's in typing_extensions
def debug(*args: TV_Loggable, **kwargs: T.Any) -> None:
def debug(*args: TV_Loggable, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
arr = process_markup(args, False)
if log_file is not None:
print(*arr, file=log_file, **kwargs)
print(*arr, file=log_file, sep=sep, end=end)
log_file.flush()
def _debug_log_cmd(cmd: str, args: T.List[str]) -> None:
@ -260,27 +264,30 @@ def cmd_ci_include(file: str) -> None:
def log(*args: TV_Loggable, is_error: bool = False,
once: bool = False, **kwargs: T.Any) -> None:
once: bool = False, nested: bool = True,
sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
if once:
log_once(*args, is_error=is_error, **kwargs)
log_once(*args, is_error=is_error, nested=nested, sep=sep, end=end)
else:
_log(*args, is_error=is_error, **kwargs)
_log(*args, is_error=is_error, nested=nested, sep=sep, end=end)
def _log(*args: TV_Loggable, is_error: bool = False,
**kwargs: T.Any) -> None:
nested = kwargs.pop('nested', True)
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
arr = process_markup(args, False)
if log_file is not None:
print(*arr, file=log_file, **kwargs)
print(*arr, file=log_file, sep=sep, end=end)
log_file.flush()
if colorize_console():
arr = process_markup(args, True)
if not log_errors_only or is_error:
force_print(*arr, nested=nested, **kwargs)
force_print(*arr, nested=nested, sep=sep, end=end)
def log_once(*args: TV_Loggable, is_error: bool = False,
**kwargs: T.Any) -> None:
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
"""Log variant that only prints a given message one time per meson invocation.
This considers ansi decorated values by the values they wrap without
@ -296,7 +303,7 @@ def log_once(*args: TV_Loggable, is_error: bool = False,
if t in _logged_once:
return
_logged_once.add(t)
_log(*args, is_error=is_error, **kwargs)
_log(*args, is_error=is_error, nested=nested, sep=sep, end=end)
# This isn't strictly correct. What we really want here is something like:
# class StringProtocol(typing_extensions.Protocol):
@ -309,7 +316,11 @@ def get_error_location_string(fname: str, lineno: int) -> str:
return f'{fname}:{lineno}:'
def _log_error(severity: str, *rargs: TV_Loggable,
once: bool = False, fatal: bool = True, **kwargs: T.Any) -> None:
once: bool = False, fatal: bool = True,
location: T.Optional[BaseNode] = None,
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None,
is_error: bool = True) -> None:
from .mesonlib import MesonException, relpath
# The typing requirements here are non-obvious. Lists are invariant,
@ -327,7 +338,6 @@ def _log_error(severity: str, *rargs: TV_Loggable,
# rargs is a tuple, not a list
args = label + list(rargs)
location = kwargs.pop('location', None)
if location is not None:
location_file = relpath(location.filename, os.getcwd())
location_str = get_error_location_string(location_file, location.lineno)
@ -336,7 +346,7 @@ def _log_error(severity: str, *rargs: TV_Loggable,
location_list = T.cast('TV_LoggableList', [location_str])
args = location_list + args
log(*args, once=once, **kwargs)
log(*args, once=once, nested=nested, sep=sep, end=end, is_error=is_error)
global log_warnings_counter # pylint: disable=global-statement
log_warnings_counter += 1
@ -344,17 +354,37 @@ def _log_error(severity: str, *rargs: TV_Loggable,
if log_fatal_warnings and fatal:
raise MesonException("Fatal warnings enabled, aborting")
def error(*args: TV_Loggable, **kwargs: T.Any) -> None:
return _log_error('error', *args, **kwargs, is_error=True)
def warning(*args: TV_Loggable, **kwargs: T.Any) -> None:
return _log_error('warning', *args, **kwargs, is_error=True)
def deprecation(*args: TV_Loggable, **kwargs: T.Any) -> None:
return _log_error('deprecation', *args, **kwargs, is_error=True)
def notice(*args: TV_Loggable, **kwargs: T.Any) -> None:
return _log_error('notice', *args, **kwargs, is_error=False)
def error(*args: TV_Loggable,
once: bool = False, fatal: bool = True,
location: T.Optional[BaseNode] = None,
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
return _log_error('error', *args, once=once, fatal=fatal, location=location,
nested=nested, sep=sep, end=end, is_error=True)
def warning(*args: TV_Loggable,
once: bool = False, fatal: bool = True,
location: T.Optional[BaseNode] = None,
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
return _log_error('warning', *args, once=once, fatal=fatal, location=location,
nested=nested, sep=sep, end=end, is_error=True)
def deprecation(*args: TV_Loggable,
once: bool = False, fatal: bool = True,
location: T.Optional[BaseNode] = None,
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
return _log_error('deprecation', *args, once=once, fatal=fatal, location=location,
nested=nested, sep=sep, end=end, is_error=True)
def notice(*args: TV_Loggable,
once: bool = False, fatal: bool = True,
location: T.Optional[BaseNode] = None,
nested: bool = True, sep: T.Optional[str] = None,
end: T.Optional[str] = None) -> None:
return _log_error('notice', *args, once=once, fatal=fatal, location=location,
nested=nested, sep=sep, end=end, is_error=False)
def get_relative_path(target: Path, current: Path) -> Path:
"""Get the path to target from current"""

Loading…
Cancel
Save