From 6a3a7a6e26240effeca58894ba1976464d1a6151 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Mon, 11 Nov 2024 17:04:52 +0000 Subject: [PATCH] Removed colorer and ansiterm modules (#105) --- README.md | 3 +- demo/demo_colorer.py | 12 -- pyproject.toml | 1 - src/tendo/__init__.py | 2 - src/tendo/ansiterm.py | 300 -------------------------------- src/tendo/colorer.py | 150 ---------------- src/tendo/tee.py | 1 - src/tendo/tests/test_colorer.py | 42 ----- tox.ini | 4 +- 9 files changed, 3 insertions(+), 512 deletions(-) delete mode 100755 demo/demo_colorer.py delete mode 100755 src/tendo/ansiterm.py delete mode 100755 src/tendo/colorer.py delete mode 100644 src/tendo/tests/test_colorer.py diff --git a/README.md b/README.md index 9cad3d2..a980e59 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,8 @@ not provided by Python. - [transparent Unicode support for text file operations (BOM detection)](https://tendo.readthedocs.io/#module-tendo.singleton) -- [console logging coloring](https://tendo.readthedocs.io/#module-tendo.colorer) - enable you to use symlinks under windows -- [python tee implementation](https://tendo.readthedocs.io/#module-tendo.colorer) for executing external programs and redirecting their output to both console/file +- python tee implementation - [improved execfile](https://tendo.readthedocs.io/#module-tendo.execfile2) ## Requirements and Compatibility diff --git a/demo/demo_colorer.py b/demo/demo_colorer.py deleted file mode 100755 index f4a5550..0000000 --- a/demo/demo_colorer.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python - -from tendo import colorer # noqa - -if __name__ == "__main__": - import logging - - logging.getLogger().setLevel(logging.NOTSET) - logging.warning("a warning") - logging.error("some error") - logging.info("some info") - logging.debug("some info") diff --git a/pyproject.toml b/pyproject.toml index 30bd76c..c1974ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,6 @@ keywords = [ "tendo", "tee", "unicode", - "colorer", "singleton", ] diff --git a/src/tendo/__init__.py b/src/tendo/__init__.py index 261ab13..a0e7421 100644 --- a/src/tendo/__init__.py +++ b/src/tendo/__init__.py @@ -11,11 +11,9 @@ __status__ = "Production" __all__ = ( "tee", - "colorer", "unicode", "execfile2", "singleton", - "ansiterm", "__version__", ) diff --git a/src/tendo/ansiterm.py b/src/tendo/ansiterm.py deleted file mode 100755 index aa8404b..0000000 --- a/src/tendo/ansiterm.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/env python -# Originally from http://waf.googlecode.com/svn/trunk/waflib/ansiterm.py -import os -import sys - -try: - if (not sys.stderr.isatty()) or (not sys.stdout.isatty()): - raise ValueError("not a tty") - - import ctypes - from ctypes import byref, c_char, c_int, c_short, windll - - class COORD(ctypes.Structure): - _fields_ = [("X", c_short), ("Y", c_short)] - - class SMALL_RECT(ctypes.Structure): - _fields_ = [ - ("Left", c_short), - ("Top", c_short), - ("Right", c_short), - ("Bottom", c_short), - ] - - class CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure): - _fields_ = [ - ("Size", COORD), - ("CursorPosition", COORD), - ("Attributes", c_short), - ("Window", SMALL_RECT), - ("MaximumWindowSize", COORD), - ] - - class CONSOLE_CURSOR_INFO(ctypes.Structure): - _fields_ = [("dwSize", ctypes.c_ulong), ("bVisible", c_int)] - - sbinfo = CONSOLE_SCREEN_BUFFER_INFO() - csinfo = CONSOLE_CURSOR_INFO() - hconsole = windll.kernel32.GetStdHandle(-11) - windll.kernel32.GetConsoleScreenBufferInfo(hconsole, byref(sbinfo)) - if sbinfo.Size.X < 10 or sbinfo.Size.Y < 10: - raise Exception("small console") - windll.kernel32.GetConsoleCursorInfo(hconsole, byref(csinfo)) -except Exception: - pass -else: - import re - import threading - - def to_int(number, default): - return number and int(number) or default - - wlock = threading.Lock() - - STD_OUTPUT_HANDLE = -11 - STD_ERROR_HANDLE = -12 - - class AnsiTerm: - def __init__(self): - self.encoding = sys.stdout.encoding - self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) - self.cursor_history = [] - self.orig_sbinfo = CONSOLE_SCREEN_BUFFER_INFO() - self.orig_csinfo = CONSOLE_CURSOR_INFO() - windll.kernel32.GetConsoleScreenBufferInfo( - self.hconsole, - byref(self.orig_sbinfo), - ) - windll.kernel32.GetConsoleCursorInfo(hconsole, byref(self.orig_csinfo)) - - def screen_buffer_info(self): - sbinfo = CONSOLE_SCREEN_BUFFER_INFO() - windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(sbinfo)) - return sbinfo - - def clear_line(self, param): - mode = param and int(param) or 0 - sbinfo = self.screen_buffer_info() - if mode == 1: # Clear from begining of line to cursor position - line_start = COORD(0, sbinfo.CursorPosition.Y) - line_length = sbinfo.Size.X - elif mode == 2: # Clear entire line - line_start = COORD(sbinfo.CursorPosition.X, sbinfo.CursorPosition.Y) - line_length = sbinfo.Size.X - sbinfo.CursorPosition.X - else: # Clear from cursor position to end of line - line_start = sbinfo.CursorPosition - line_length = sbinfo.Size.X - sbinfo.CursorPosition.X - chars_written = c_int() - windll.kernel32.FillConsoleOutputCharacterA( - self.hconsole, - c_char(" "), - line_length, - line_start, - byref(chars_written), - ) - windll.kernel32.FillConsoleOutputAttribute( - self.hconsole, - sbinfo.Attributes, - line_length, - line_start, - byref(chars_written), - ) - - def clear_screen(self, param): - mode = to_int(param, 0) - sbinfo = self.screen_buffer_info() - if mode == 1: # Clear from begining of screen to cursor position - clear_start = COORD(0, 0) - clear_length = sbinfo.CursorPosition.X * sbinfo.CursorPosition.Y - elif mode == 2: # Clear entire screen and return cursor to home - clear_start = COORD(0, 0) - clear_length = sbinfo.Size.X * sbinfo.Size.Y - windll.kernel32.SetConsoleCursorPosition(self.hconsole, clear_start) - else: # Clear from cursor position to end of screen - clear_start = sbinfo.CursorPosition - clear_length = ( - sbinfo.Size.X - - sbinfo.CursorPosition.X - + sbinfo.Size.X * (sbinfo.Size.Y - sbinfo.CursorPosition.Y) - ) - chars_written = c_int() - windll.kernel32.FillConsoleOutputCharacterA( - self.hconsole, - c_char(" "), - clear_length, - clear_start, - byref(chars_written), - ) - windll.kernel32.FillConsoleOutputAttribute( - self.hconsole, - sbinfo.Attributes, - clear_length, - clear_start, - byref(chars_written), - ) - - def push_cursor(self, param): - sbinfo = self.screen_buffer_info() - self.cursor_history.push(sbinfo.CursorPosition) - - def pop_cursor(self, param): - if self.cursor_history: - old_pos = self.cursor_history.pop() - windll.kernel32.SetConsoleCursorPosition(self.hconsole, old_pos) - - def set_cursor(self, param): - x, sep, y = param.partition(";") - x = to_int(x, 1) - 1 - y = to_int(y, 1) - 1 - sbinfo = self.screen_buffer_info() - new_pos = COORD( - min(max(0, x), sbinfo.Size.X), - min(max(0, y), sbinfo.Size.Y), - ) - windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) - - def set_column(self, param): - x = to_int(param, 1) - 1 - sbinfo = self.screen_buffer_info() - new_pos = COORD(min(max(0, x), sbinfo.Size.X), sbinfo.CursorPosition.Y) - windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) - - def move_cursor(self, x_offset=0, y_offset=0): - sbinfo = self.screen_buffer_info() - new_pos = COORD( - min(max(0, sbinfo.CursorPosition.X + x_offset), sbinfo.Size.X), - min(max(0, sbinfo.CursorPosition.Y + y_offset), sbinfo.Size.Y), - ) - windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) - - def move_up(self, param): - self.move_cursor(y_offset=-to_int(param, 1)) - - def move_down(self, param): - self.move_cursor(y_offset=to_int(param, 1)) - - def move_left(self, param): - self.move_cursor(x_offset=-to_int(param, 1)) - - def move_right(self, param): - self.move_cursor(x_offset=to_int(param, 1)) - - def next_line(self, param): - sbinfo = self.screen_buffer_info() - self.move_cursor( - x_offset=-sbinfo.CursorPosition.X, - y_offset=to_int(param, 1), - ) - - def prev_line(self, param): - sbinfo = self.screen_buffer_info() - self.move_cursor( - x_offset=-sbinfo.CursorPosition.X, - y_offset=-to_int(param, 1), - ) - - escape_to_color = { - (0, 30): 0x0, # black - (0, 31): 0x4, # red - (0, 32): 0x2, # green - (0, 33): 0x4 + 0x2, # dark yellow - (0, 34): 0x1, # blue - (0, 35): 0x1 + 0x4, # purple - (0, 36): 0x2 + 0x4, # cyan - (0, 37): 0x1 + 0x2 + 0x4, # grey - (1, 30): 0x1 + 0x2 + 0x4, # dark gray - (1, 31): 0x4 + 0x8, # red - (1, 32): 0x2 + 0x8, # light green - (1, 33): 0x4 + 0x2 + 0x8, # yellow - (1, 34): 0x1 + 0x8, # light blue - (1, 35): 0x1 + 0x4 + 0x8, # light purple - (1, 36): 0x1 + 0x2 + 0x8, # light cyan - (1, 37): 0x1 + 0x2 + 0x4 + 0x8, # white - } - - def set_color(self, param): - cols = param.split(";") - attr = self.orig_sbinfo.Attributes - for c in cols: - c = to_int(c, 0) - if c in range(30, 38): - attr = (attr & 0xF0) | (self.escape_to_color.get((0, c), 0x7)) - elif c in range(40, 48): - attr = (attr & 0x0F) | (self.escape_to_color.get((0, c), 0x7) << 8) - elif c in range(90, 98): - attr = (attr & 0xF0) | (self.escape_to_color.get((1, c - 60), 0x7)) - elif c in range(100, 108): - attr = (attr & 0x0F) | ( - self.escape_to_color.get((1, c - 60), 0x7) << 8 - ) - elif c == 1: - attr |= 0x08 - windll.kernel32.SetConsoleTextAttribute(self.hconsole, attr) - - def show_cursor(self, param): - csinfo.bVisible = 1 - windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo)) - - def hide_cursor(self, param): - csinfo.bVisible = 0 - windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo)) - - ansi_command_table = { - "A": move_up, - "B": move_down, - "C": move_right, - "D": move_left, - "E": next_line, - "F": prev_line, - "G": set_column, - "H": set_cursor, - "f": set_cursor, - "J": clear_screen, - "K": clear_line, - "h": show_cursor, - "l": hide_cursor, - "m": set_color, - "s": push_cursor, - "u": pop_cursor, - } - # Match either the escape sequence or text not containing escape - # sequence - ansi_tokans = re.compile(r"(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))") - - def write(self, text): - try: - wlock.acquire() - for param, cmd, txt in self.ansi_tokans.findall(text): - if cmd: - cmd_func = self.ansi_command_table.get(cmd) - if cmd_func: - cmd_func(self, param) - else: - chars_written = c_int() - if isinstance(txt, str): - windll.kernel32.WriteConsoleW( - self.hconsole, - txt, - len(txt), - byref(chars_written), - None, - ) - else: - windll.kernel32.WriteConsoleA( - self.hconsole, - txt, - len(txt), - byref(chars_written), - None, - ) - finally: - wlock.release() - - def flush(self): - pass - - def isatty(self): - return True - - sys.stderr = sys.stdout = AnsiTerm() - os.environ["TERM"] = "vt100" diff --git a/src/tendo/colorer.py b/src/tendo/colorer.py deleted file mode 100755 index a87eb81..0000000 --- a/src/tendo/colorer.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python - -""" -Colorer does enable colored logging messages by using `ANSI escape sequences `_. - -Under Windows, where the escapes are not supported it does use the Windows API. - -The colored output is generated only when the console is a terminal supporting it, so if you redirect the output to a log file you will not see the escape codes in the file. - ->>> import colorer, logging -... logging.error("red line") -... logging.warn("yellow line") -... logging.info("gray line") -... logging.debug("magenta line") -""" - -import copy -import logging -import os -import sys - -if ( - (hasattr(sys.stderr, "isatty") and sys.stderr.isatty()) - or ("TERM" in os.environ.keys() and os.environ["TERM"] in ["linux"]) - or ("PYCHARM_HOSTED" in os.environ.keys()) -): - # Why stderr and not stdout? - because python logging module does output to stderr by default and not stdout. - # now we patch Python code to add color support to logging.StreamHandler - def add_coloring_to_emit_windows(fn): - # add methods we need to the class - def _out_handle(self): - import ctypes - - return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) - - def _set_color(self, code): - import ctypes - - # Constants from the Windows API - self.STD_OUTPUT_HANDLE = -11 - hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE) - ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code) - - logging.StreamHandler._set_color = _set_color - - def new(*args): - FOREGROUND_BLUE = 0x0001 # text color contains blue. - FOREGROUND_GREEN = 0x0002 # text color contains green. - FOREGROUND_RED = 0x0004 # text color contains red. - FOREGROUND_INTENSITY = 0x0008 # text color is intensified. - FOREGROUND_WHITE = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED - # winbase.h - # STD_INPUT_HANDLE = -10 - # STD_OUTPUT_HANDLE = -11 - # STD_ERROR_HANDLE = -12 - - # wincon.h - # FOREGROUND_BLACK = 0x0000 - FOREGROUND_BLUE = 0x0001 - FOREGROUND_GREEN = 0x0002 - # FOREGROUND_CYAN = 0x0003 - FOREGROUND_RED = 0x0004 - FOREGROUND_MAGENTA = 0x0005 - FOREGROUND_YELLOW = 0x0006 - # FOREGROUND_GREY = 0x0007 - FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified. - - # BACKGROUND_BLACK = 0x0000 - # BACKGROUND_BLUE = 0x0010 - # BACKGROUND_GREEN = 0x0020 - # BACKGROUND_CYAN = 0x0030 - # BACKGROUND_RED = 0x0040 - # BACKGROUND_MAGENTA = 0x0050 - BACKGROUND_YELLOW = 0x0060 - # BACKGROUND_GREY = 0x0070 - BACKGROUND_INTENSITY = 0x0080 # background color is intensified. - - levelno = args[1].levelno - if levelno >= 50: - color = ( - BACKGROUND_YELLOW - | FOREGROUND_RED - | FOREGROUND_INTENSITY - | BACKGROUND_INTENSITY - ) - elif levelno >= 40: - color = FOREGROUND_RED | FOREGROUND_INTENSITY - elif levelno >= 30: - color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY - elif levelno >= 20: - color = FOREGROUND_GREEN - elif levelno >= 10: - color = FOREGROUND_MAGENTA - else: - color = FOREGROUND_WHITE - args[0]._set_color(color) - - ret = fn(*args) - args[0]._set_color(FOREGROUND_WHITE) - # print "after" - return ret - - return new - - def add_coloring_to_emit_ansi(fn): - # add methods we need to the class - def new(*args): - # new_args = args - if len(args) == 2: - new_args = (args[0], copy.copy(args[1])) - else: - new_args = (args[0], copy.copy(args[1]), args[2:]) - if hasattr(args[0], "baseFilename"): - return fn(*args) - levelno = new_args[1].levelno - if levelno >= 50 or levelno >= 40: - color = "\x1b[31m" # red - elif levelno >= 30: - color = "\x1b[33m" # yellow - elif levelno >= 20: - color = "\x1b[32m" # green - elif levelno >= 10: - color = "\x1b[35m" # pink - else: - color = "\x1b[0m" # normal - try: - new_args[1].msg = color + str(new_args[1].msg) + "\x1b[0m" # normal - except Exception as e: - raise e - return fn(*new_args) - - return new - - import platform - - if platform.system() == "Windows": - # Windows does not support ANSI escapes and we are using API calls to - # set the console color - logging.StreamHandler.emit = add_coloring_to_emit_windows( - logging.StreamHandler.emit, - ) - else: - # all non-Windows platforms are supporting ANSI escapes so we use them - logging.StreamHandler.emit = add_coloring_to_emit_ansi( - logging.StreamHandler.emit, - ) - # log = logging.getLogger() - # log.addFilter(log_filter()) - # //hdlr = logging.StreamHandler() - # //hdlr.setFormatter(formatter()) diff --git a/src/tendo/tee.py b/src/tendo/tee.py index ee44950..a05801f 100755 --- a/src/tendo/tee.py +++ b/src/tendo/tee.py @@ -265,7 +265,6 @@ def test_4(self): # import pytest # pytest.main(['--pyargs', __name__]) """ - import colorer import tempfile, os logging.basicConfig(level=logging.NOTSET, diff --git a/src/tendo/tests/test_colorer.py b/src/tendo/tests/test_colorer.py deleted file mode 100644 index a2e2e73..0000000 --- a/src/tendo/tests/test_colorer.py +++ /dev/null @@ -1,42 +0,0 @@ -import logging -import sys -import tempfile - - -def test_colorer(): - isatty = None - if hasattr(sys.stderr, "isatty"): - isatty = sys.stderr.isatty() - print("sys.stderr.isatty = %s" % isatty) - - isatty = None - if hasattr(sys.stdout, "isatty"): - isatty = sys.stdout.isatty() - print("sys.stdout.isatty = %s" % isatty) - - logging.getLogger().setLevel(logging.NOTSET) - tmp_file = tempfile.NamedTemporaryFile(suffix="_colorer.log").name - fh = logging.FileHandler(tmp_file) - fh.setLevel(logging.NOTSET) - ch = logging.StreamHandler() - ch.setLevel(logging.NOTSET) - formatter = logging.Formatter("%(levelname)s: %(message)s") - fh.setFormatter(formatter) - ch.setFormatter(formatter) - logging.getLogger().addHandler(ch) - logging.getLogger().addHandler(fh) - - logging.warning("a warning") - logging.error("some error") - logging.info("some info") - logging.debug("some info") - expected_lines = [ - "WARNING: a warning\n", - "ERROR: some error\n", - "INFO: some info\n", - "DEBUG: some info\n", - ] - line_no = 0 - for line in open(tmp_file).readlines(): - assert line == expected_lines[line_no] - line_no += 1 diff --git a/tox.ini b/tox.ini index dcb559d..ba7068a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -minversion = 3.0 +minversion = 4.23.2 envlist = lint pkg @@ -52,7 +52,7 @@ allowlist_externals = deps = pre-commit>=3.3.3 commands= - python -m pre_commit run --all + {envpython} -m pre_commit run --all [testenv:pkg] description =