diff --git a/duties/cli/arguments.py b/duties/cli/arguments.py index c40c5cb9..deb31f85 100644 --- a/duties/cli/arguments.py +++ b/duties/cli/arguments.py @@ -50,6 +50,32 @@ def __get_raw_arguments() -> Namespace: action="store_true", default=False, ) + parser.add_argument( + "--log-color-warning", + type=parse.set_logging_color, + help=( + "The logging color as hex or rgb code for warning logs (default: '255,255,0' - yellow)" + ), + action="store", + default=[255, 255, 0], + ) + parser.add_argument( + "--log-color-critical", + type=parse.set_logging_color, + help="The logging color as hex or rgb code for critical logs (default: '255, 0, 0' - red)", + action="store", + default=[255, 0, 0], + ) + parser.add_argument( + "--log-color-proposing", + type=parse.set_logging_color, + help=( + "The logging color as hex or rgb code for proposing duty logs " + "(default: '0, 128, 0' - green)" + ), + action="store", + default=[0, 128, 0], + ) parser.add_argument( "--log-time-warning", type=float, diff --git a/duties/cli/parse.py b/duties/cli/parse.py index 34423806..6a0531e6 100644 --- a/duties/cli/parse.py +++ b/duties/cli/parse.py @@ -3,6 +3,8 @@ from typing import List +from constants.program import HEX_COLOR_STARTING_POSITIONS, HEX_TO_INT_BASE + def set_validator_identifiers(validators: str) -> List[str]: """Parse provided validators for space and comma separation @@ -42,3 +44,32 @@ def set_beacon_node_urls(beacon_node_urls: str) -> List[str]: if not beacon_node_urls.startswith(("http://", "https://")): raise ValueError() return [beacon_node_urls] + + +def set_logging_color(logging_color: str) -> List[int]: + """Parse provided logging color + + Args: + logging_color (str): Logging color in hex or RGB code + + Raises: + ValueError: Error if RGB codes are not in range 0-255 + + Returns: + List[int]: RGB color code + """ + if logging_color.startswith("#"): + logging_color = logging_color[1:] + rgb_color_codes = [ + int(logging_color[position : position + 2], HEX_TO_INT_BASE) + for position in HEX_COLOR_STARTING_POSITIONS + ] + else: + string_rgb_color_codes = logging_color.split(",") + rgb_color_codes = [int(rgb_code) for rgb_code in string_rgb_color_codes] + filtered_rgb_color_codes = [ + rgb_code for rgb_code in rgb_color_codes if rgb_code >= 0 if rgb_code <= 255 + ] + if len(filtered_rgb_color_codes) != 3: + raise ValueError() + return filtered_rgb_color_codes diff --git a/duties/constants/program.py b/duties/constants/program.py index 40c678b2..d417764e 100644 --- a/duties/constants/program.py +++ b/duties/constants/program.py @@ -36,3 +36,6 @@ SECONDS_UNTIL_BEACON_NODE_CALL_ERROR_IS_LOGGED_AGAIN = 5 REST_RAW_DUTY_NO_BEACON_NODE_CONNECTION_TIMEOUT = 7 REST_ANY_DUTY_NO_BEACON_NODE_CONNECTION_TIMEOUT = 10 +HEX_COLOR_STARTING_POSITIONS = (0, 2, 4) +HEX_TO_INT_BASE = 16 +USED_STY_BACKGROUND_COLOR_NAMES = ["yellow", "red", "green"] diff --git a/duties/fetcher/__init__.py b/duties/fetcher/__init__.py index f801fb6c..eca36e72 100644 --- a/duties/fetcher/__init__.py +++ b/duties/fetcher/__init__.py @@ -3,18 +3,20 @@ import sys from logging import config as logging_config -from os import path +from os import path, system from typing import Any from cli.arguments import ARGUMENTS -from colorama import init +from constants.program import USED_STY_BACKGROUND_COLOR_NAMES +from sty import RgbBg, Style, bg # type: ignore[import] from yaml import safe_load def __initialize() -> None: """Initializes logger and colorama""" + system("") __initialize_logging(ARGUMENTS.log) - __initialize_colorama() + __set_colors() def __initialize_logging(log_level: str) -> None: @@ -49,9 +51,26 @@ def __get_logging_configuration_path() -> str: return logging_configuration_path -def __initialize_colorama() -> None: - """Initializes coloroma so that colorful logging works independent from OS""" - init() +def __set_colors() -> None: + """Overrides used color attributes of sty package""" + log_color_arguments = [ + argument + # pylint: disable-next=protected-access + for argument in ARGUMENTS._get_kwargs() + if argument[0].startswith("log_color") + ] + for index, color_name in enumerate(USED_STY_BACKGROUND_COLOR_NAMES): + setattr( + bg, + color_name, + Style( + RgbBg( + log_color_arguments[index][1][0], + log_color_arguments[index][1][1], + log_color_arguments[index][1][2], + ) + ), + ) __initialize() diff --git a/duties/fetcher/data_types.py b/duties/fetcher/data_types.py index c8f1a0c1..ee30563b 100644 --- a/duties/fetcher/data_types.py +++ b/duties/fetcher/data_types.py @@ -5,7 +5,7 @@ from enum import Enum from typing import List -from dataclass_wizard import JSONWizard +from dataclass_wizard import JSONWizard # type: ignore[import] class DutyType(Enum): diff --git a/duties/fetcher/log.py b/duties/fetcher/log.py index dbf30bfc..4d768b46 100644 --- a/duties/fetcher/log.py +++ b/duties/fetcher/log.py @@ -7,11 +7,11 @@ from typing import List, Tuple from cli.arguments import ARGUMENTS -from colorama import Back, Style from constants import logging, program from fetcher.data_types import DutyType, ValidatorDuty, ValidatorIdentifier from fetcher.identifier.core import read_validator_identifiers_from_shared_memory from protocol import ethereum +from sty import bg, rs # type: ignore[import] __validator_identifiers_with_alias = {"0": ValidatorIdentifier()} @@ -71,7 +71,7 @@ def __create_logging_message(duty: ValidatorDuty) -> str: f"{__get_logging_color(duty.time_to_duty, duty)}" f"Validator {__get_validator_identifier_for_logging(duty)} " f"has next {duty.type.name} duty in: " - f"{time_to_next_duty} min. (slot: {duty.slot}){Style.RESET_ALL}" + f"{time_to_next_duty} min. (slot: {duty.slot}){rs.all}" ) return logging_message @@ -94,17 +94,17 @@ def __create_sync_committee_logging_message(sync_committee_duty: ValidatorDuty) ) if sync_committee_duty.time_to_duty == 0: logging_message = ( - f"{Back.RED}Validator {__get_validator_identifier_for_logging(sync_committee_duty)} " + f"{bg.red}Validator {__get_validator_identifier_for_logging(sync_committee_duty)} " f"is in current sync committee (next sync committee starts in " f"{time_to_next_sync_committee} / " - f"epoch: {current_sync_committee_epoch_boundaries[1] + 1}){Style.RESET_ALL}" + f"epoch: {current_sync_committee_epoch_boundaries[1] + 1}){rs.all}" ) else: logging_message = ( - f"{Back.YELLOW}Validator " + f"{bg.yellow}Validator " f"{__get_validator_identifier_for_logging(sync_committee_duty)} will be in next " f"sync committee which starts in {time_to_next_sync_committee} " - f"(epoch: {current_sync_committee_epoch_boundaries[1] + 1}){Style.RESET_ALL}" + f"(epoch: {current_sync_committee_epoch_boundaries[1] + 1}){rs.all}" ) return logging_message @@ -144,12 +144,12 @@ def __get_logging_color(seconds_to_next_duty: float, duty: ValidatorDuty) -> str str: ANSI codes for colorful logging """ if ARGUMENTS.log_time_critical < seconds_to_next_duty <= ARGUMENTS.log_time_warning: - return Back.YELLOW + return bg.yellow if seconds_to_next_duty <= ARGUMENTS.log_time_critical: - return Back.RED + return bg.red if duty.type is DutyType.PROPOSING: - return Back.GREEN - return Style.RESET_ALL + return bg.green + return rs.all def __get_validator_identifier_for_logging(duty: ValidatorDuty) -> str: diff --git a/poetry.lock b/poetry.lock index a79b0231..39f8aad4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -26,25 +26,25 @@ files = [ [[package]] name = "anyio" -version = "3.7.1" +version = "4.0.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, + {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.22)"] [[package]] name = "astroid" @@ -717,14 +717,14 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2023.7" +version = "2023.8" description = "Community maintained hooks for PyInstaller" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pyinstaller-hooks-contrib-2023.7.tar.gz", hash = "sha256:0c436a4c3506020e34116a8a7ddfd854c1ad6ddca9a8cd84500bd6e69c9e68f9"}, - {file = "pyinstaller_hooks_contrib-2023.7-py2.py3-none-any.whl", hash = "sha256:3c10df14c0f71ab388dfbf1625375b087e7330d9444cbfd2b310ba027fa0cff0"}, + {file = "pyinstaller-hooks-contrib-2023.8.tar.gz", hash = "sha256:318ccc316fb2b8c0bbdff2456b444bf1ce0e94cb3948a0f4dd48f6fc33d41c01"}, + {file = "pyinstaller_hooks_contrib-2023.8-py2.py3-none-any.whl", hash = "sha256:d091a52fbeed71cde0359aa9ad66288521a8441cfba163d9446606c5136c72a8"}, ] [[package]] @@ -880,20 +880,20 @@ doc = ["pytoolconfig[doc]", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.1 [[package]] name = "setuptools" -version = "68.1.2" +version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, - {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "sniffio" @@ -925,6 +925,17 @@ anyio = ">=3.4.0,<5" [package.extras] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] +[[package]] +name = "sty" +version = "1.0.4" +description = "String styling for your terminal" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "sty-1.0.4-py3-none-any.whl", hash = "sha256:3e3352f05770a37e11c9dfc303cbccb1c6acc13710e15d181cb43be7df89f8ea"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -949,18 +960,6 @@ files = [ {file = "tomlkit-0.12.1.tar.gz", hash = "sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86"}, ] -[[package]] -name = "types-colorama" -version = "0.4.15.12" -description = "Typing stubs for colorama" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "types-colorama-0.4.15.12.tar.gz", hash = "sha256:fbdfc5d9d24d85c33bd054fbe33adc6cec44eedb19cfbbabfbbb57dc257ae4b8"}, - {file = "types_colorama-0.4.15.12-py3-none-any.whl", hash = "sha256:23c9d4a00961227f7ef018d5a1c190c4bbc282119c3ee76a17677a793f13bb82"}, -] - [[package]] name = "types-pyyaml" version = "6.0.12.11" @@ -1002,26 +1001,26 @@ files = [ [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "urllib3" -version = "2.0.4" +version = "2.0.5" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, - {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, + {file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"}, + {file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"}, ] [package.extras] @@ -1138,4 +1137,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10.0,<3.11" -content-hash = "558cfb0e9b9b4f8e04a45443464eee55a9ff8f052957723c6028fb8ef0a07ae0" +content-hash = "0f472bd8f5050718c80dcbac127ffe0b223b8e1699286234bee3684b7b2c2d61" diff --git a/pyproject.toml b/pyproject.toml index b4ea98de..11c9897c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,13 +8,13 @@ repository = "https://github.com/TobiWo/eth-duties" version = "0.4.0" [tool.poetry.dependencies] -colorama = "==0.4.6" dataclass-wizard = "==0.22.0" eth-typing = "==3.4.0" fastapi = "==0.103.0" python = ">=3.10.0,<3.11" pyyaml = "==6.0.1" requests = "==2.31.0" +sty = "==1.0.4" uvicorn = "==0.23.2" [tool.poetry.group.dev.dependencies] @@ -25,7 +25,6 @@ pyinstaller = "==4.10.0" pylint = "==2.17.5" rope = "==1.4.0" types-PyYAML = "==6.0.12.11" -types-colorama = "==0.4.15.12" types-requests = "==2.31.0.2" [build-system]