wesnoth/data/tools/pywmlx/wmlerr.py

160 lines
4.8 KiB
Python

import os
import sys
import warnings
import ctypes
import struct
enabled_text_col = True
is_utest = False
_warnall = False
# these constants are used by the Win32 API
FG_RED = 4
FG_GREEN = 2
FG_BLUE = 1
FG_INTENSITY = 8
BG_RED = 64
BG_GREEN = 32
BG_BLUE = 16
BG_INTENSITY = 128
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
# there are three ways to store the console's default status
# the first requires to redefine the C structures used by the Win32 API
# by using the ctypes.Structure class, and pass them by reference
# by using ctypes.byref
# the second requires to use ctypes.create_string_buffer
# the third one just requires to create an empty bytes object
# both of them must be able to contain exactly 22 bytes/characters
# and you need to use the struct module to decode the values
console_defaults = bytes(22)
def wmlerr_debug():
global is_utest
is_utest = True
def ansi_setEnabled(value):
global enabled_text_col
enabled_text_col = value
def wincol_setEnabled(value):
global enabled_text_col
global handle
global default_console_status
global default_color
enabled_text_col = value
if sys.platform.startswith('win') and enabled_text_col:
# and now, let's start playing with the Win32 API
# first of all, we need to get a handle to the console output
handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
# and then we store the current console status
# to be able to reset the original color
ctypes.windll.kernel32.GetConsoleScreenBufferInfo(handle,
console_defaults)
# by using struct.unpack_from, platform endianness is automatically
# handled
# this is why I'm using it, instead of a bitwise operation
default_color = struct.unpack_from("H", console_defaults, 8)[0]
def warnall():
return _warnall
def set_warnall(value):
_warnall = value
class WmlError(ValueError):
pass
class WmlWarning(UserWarning):
pass
def print_wmlerr(finfo, message, iserr):
# red if error, blue if warning
ansi_color = '\033[91;1m' if iserr else '\033[94m'
errtype = "error:" if iserr else "warning:"
# now we have ascii_color and errtype values
# here we print the error/warning.
# 1) On Windows, write "error" in red and "warning" in blue
# by using the Win32 API (except if --no-text-colors is used)
if sys.platform.startswith('win') and enabled_text_col:
# a syntactic sugar to make lines shorter
kernel32 = ctypes.windll.kernel32
# first flush the stderr, otherwise colors might be changed in
# unpredictable ways
sys.stderr.flush()
if iserr:
kernel32.SetConsoleTextAttribute(handle, FG_RED | FG_INTENSITY)
else:
kernel32.SetConsoleTextAttribute(handle, FG_BLUE | FG_INTENSITY)
# then write the error type and continue on the same line
print(errtype + " ", end="", file=sys.stderr)
# flush again and write file name in yellow on the same line
sys.stderr.flush()
kernel32.SetConsoleTextAttribute(handle,
FG_RED | FG_GREEN | FG_INTENSITY)
print(finfo + ": ", end="", file=sys.stderr)
# then flush again, reset the color and finish writing
sys.stderr.flush()
ctypes.windll.kernel32.SetConsoleTextAttribute(handle, default_color)
print(message, file=sys.stderr)
# finally flush again for good measure
sys.stderr.flush()
# 2) On posix we write "error" in red and "warning" in blue
# by using ansi escape codes (except if --no-text-colors is used)
elif os.name == "posix" and enabled_text_col:
msg = (ansi_color + errtype + ' \033[0m\033[93m' + finfo +
':\033[0m ' + message)
print(msg, file=sys.stderr)
# 3) On non-posix and non-windows system we don't use colors
# (is it ever possible?).
# If --no-text-colors option is used, than we don't use colors
# regardless of OS.
else:
msg = errtype + ' ' + finfo + ': ' + message
print(msg, file=sys.stderr)
def my_showwarning(message, category, filename, lineno, file=None, line=None):
try:
finfo, msg = message.args[0].split(": ", 1)
print_wmlerr(finfo, msg, False)
except OSError:
pass # the file (probably stderr) is invalid - this warning gets lost.
warnings.showwarning = my_showwarning
def wmlerr(finfo, message, errtype=WmlError):
if not is_utest:
try:
raise errtype(finfo + ": " + message)
except errtype as err:
print_wmlerr(finfo, message, True)
sys.exit(1)
else:
raise errtype(finfo + ": " + message)
def wmlwarn(finfo, message):
warnings.warn(finfo + ": " + message, WmlWarning)