From 90e51799805c371ca66685264171621369636b30 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 20 Nov 2025 10:59:44 +0900 Subject: [PATCH] Remove text color handling from corelibs and use corelibs_text_colors instead Also update enum with proper pyi file for deprecation warnings --- pyproject.toml | 3 + src/corelibs/logging_handling/log.py | 2 +- src/corelibs/string_handling/text_colors.py | 148 +---- src/corelibs/var_handling/enum_base.py | 80 +-- src/corelibs/var_handling/enum_base.pyi | 15 + .../unit/string_handling/test_text_colors.py | 516 ------------------ uv.lock | 22 + 7 files changed, 57 insertions(+), 729 deletions(-) create mode 100644 src/corelibs/var_handling/enum_base.pyi delete mode 100644 tests/unit/string_handling/test_text_colors.py diff --git a/pyproject.toml b/pyproject.toml index a7251b5..ec3ead6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ requires-python = ">=3.13" dependencies = [ "corelibs-datetime>=1.0.1", "corelibs-enum-base>=1.0.0", + "corelibs-text-colors>=1.0.0", "corelibs-var>=1.0.0", "cryptography>=46.0.3", "jmespath>=1.0.1", @@ -33,12 +34,14 @@ publish-url = "https://git.egplusww.jp/api/packages/PyPI/pypi" corelibs-enum-base = { index = "opj-pypi" } corelibs-datetime = { index = "opj-pypi" } corelibs-var = { index = "opj-pypi" } +corelibs-text-colors = { index = "opj-pypi" } [dependency-groups] dev = [ "deepdiff>=8.6.1", "pytest>=8.4.1", "pytest-cov>=6.2.1", + "typing-extensions>=4.15.0", ] # MARK: Python linting diff --git a/src/corelibs/logging_handling/log.py b/src/corelibs/logging_handling/log.py index 22c51f8..bc5b32b 100644 --- a/src/corelibs/logging_handling/log.py +++ b/src/corelibs/logging_handling/log.py @@ -13,8 +13,8 @@ from pathlib import Path import atexit from enum import Flag, auto from typing import MutableMapping, TextIO, TypedDict, Any, TYPE_CHECKING, cast +from corelibs_text_colors.text_colors import Colors from corelibs.logging_handling.logging_level_handling.logging_level import LoggingLevel -from corelibs.string_handling.text_colors import Colors from corelibs.debug_handling.debug_helpers import call_stack, exception_stack if TYPE_CHECKING: diff --git a/src/corelibs/string_handling/text_colors.py b/src/corelibs/string_handling/text_colors.py index 6be2e40..5b0ccae 100644 --- a/src/corelibs/string_handling/text_colors.py +++ b/src/corelibs/string_handling/text_colors.py @@ -5,152 +5,14 @@ Set colors with print(f"something {Colors.yellow}colorful{Colors.end}) bold + underline + color combinations are possible. """ +from warnings import deprecated +from corelibs_text_colors.text_colors import Colors as ColorsNew -class Colors: + +@deprecated("Use src.corelibs_text_colors.text_colors instead") +class Colors(ColorsNew): """ ANSI colors defined """ - # General sets, these should not be accessd - __BOLD = '\033[1m' - __UNDERLINE = '\033[4m' - __END = '\033[0m' - __RESET = '\033[0m' - # Define ANSI color codes as class attributes - __BLACK = "\033[30m" - __RED = "\033[31m" - __GREEN = "\033[32m" - __YELLOW = "\033[33m" - __BLUE = "\033[34m" - __MAGENTA = "\033[35m" - __CYAN = "\033[36m" - __WHITE = "\033[37m" - - # Define bold/bright versions of the colors - __BLACK_BOLD = "\033[1;30m" - __RED_BOLD = "\033[1;31m" - __GREEN_BOLD = "\033[1;32m" - __YELLOW_BOLD = "\033[1;33m" - __BLUE_BOLD = "\033[1;34m" - __MAGENTA_BOLD = "\033[1;35m" - __CYAN_BOLD = "\033[1;36m" - __WHITE_BOLD = "\033[1;37m" - - # BRIGHT, alternative - __BLACK_BRIGHT = '\033[90m' - __RED_BRIGHT = '\033[91m' - __GREEN_BRIGHT = '\033[92m' - __YELLOW_BRIGHT = '\033[93m' - __BLUE_BRIGHT = '\033[94m' - __MAGENTA_BRIGHT = '\033[95m' - __CYAN_BRIGHT = '\033[96m' - __WHITE_BRIGHT = '\033[97m' - - # set access vars - bold = __BOLD - underline = __UNDERLINE - end = __END - reset = __RESET - # normal - black = __BLACK - red = __RED - green = __GREEN - yellow = __YELLOW - blue = __BLUE - magenta = __MAGENTA - cyan = __CYAN - white = __WHITE - # bold - black_bold = __BLACK_BOLD - red_bold = __RED_BOLD - green_bold = __GREEN_BOLD - yellow_bold = __YELLOW_BOLD - blue_bold = __BLUE_BOLD - magenta_bold = __MAGENTA_BOLD - cyan_bold = __CYAN_BOLD - white_bold = __WHITE_BOLD - # bright - black_bright = __BLACK_BRIGHT - red_bright = __RED_BRIGHT - green_bright = __GREEN_BRIGHT - yellow_bright = __YELLOW_BRIGHT - blue_bright = __BLUE_BRIGHT - magenta_bright = __MAGENTA_BRIGHT - cyan_bright = __CYAN_BRIGHT - white_bright = __WHITE_BRIGHT - - @staticmethod - def disable(): - """ - No colors - """ - Colors.bold = '' - Colors.underline = '' - Colors.end = '' - Colors.reset = '' - # normal - Colors.black = '' - Colors.red = '' - Colors.green = '' - Colors.yellow = '' - Colors.blue = '' - Colors.magenta = '' - Colors.cyan = '' - Colors.white = '' - # bold/bright - Colors.black_bold = '' - Colors.red_bold = '' - Colors.green_bold = '' - Colors.yellow_bold = '' - Colors.blue_bold = '' - Colors.magenta_bold = '' - Colors.cyan_bold = '' - Colors.white_bold = '' - # bold/bright alt - Colors.black_bright = '' - Colors.red_bright = '' - Colors.green_bright = '' - Colors.yellow_bright = '' - Colors.blue_bright = '' - Colors.magenta_bright = '' - Colors.cyan_bright = '' - Colors.white_bright = '' - - @staticmethod - def reset_colors(): - """ - reset colors to the original ones - """ - # set access vars - Colors.bold = Colors.__BOLD - Colors.underline = Colors.__UNDERLINE - Colors.end = Colors.__END - Colors.reset = Colors.__RESET - # normal - Colors.black = Colors.__BLACK - Colors.red = Colors.__RED - Colors.green = Colors.__GREEN - Colors.yellow = Colors.__YELLOW - Colors.blue = Colors.__BLUE - Colors.magenta = Colors.__MAGENTA - Colors.cyan = Colors.__CYAN - Colors.white = Colors.__WHITE - # bold - Colors.black_bold = Colors.__BLACK_BOLD - Colors.red_bold = Colors.__RED_BOLD - Colors.green_bold = Colors.__GREEN_BOLD - Colors.yellow_bold = Colors.__YELLOW_BOLD - Colors.blue_bold = Colors.__BLUE_BOLD - Colors.magenta_bold = Colors.__MAGENTA_BOLD - Colors.cyan_bold = Colors.__CYAN_BOLD - Colors.white_bold = Colors.__WHITE_BOLD - # bright - Colors.black_bright = Colors.__BLACK_BRIGHT - Colors.red_bright = Colors.__RED_BRIGHT - Colors.green_bright = Colors.__GREEN_BRIGHT - Colors.yellow_bright = Colors.__YELLOW_BRIGHT - Colors.blue_bright = Colors.__BLUE_BRIGHT - Colors.magenta_bright = Colors.__MAGENTA_BRIGHT - Colors.cyan_bright = Colors.__CYAN_BRIGHT - Colors.white_bright = Colors.__WHITE_BRIGHT # __END__ diff --git a/src/corelibs/var_handling/enum_base.py b/src/corelibs/var_handling/enum_base.py index 0aa4cd1..0ce777e 100644 --- a/src/corelibs/var_handling/enum_base.py +++ b/src/corelibs/var_handling/enum_base.py @@ -2,82 +2,24 @@ Enum base classes """ -from enum import Enum -from warnings import deprecated -from typing import Any -# from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase +import warnings +from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase -class EnumBase(Enum): +class EnumBase(CorelibsEnumBase): """ base for enum + + .. deprecated:: + Use corelibs_enum_base.EnumBase instead + DEPRECATED: Use corelibs_enum_base.EnumBase instead + lookup_any and from_any will return "EnumBase" and the sub class name run the return again to "from_any" to get a clean value, or cast it """ - @classmethod - @deprecated("Use corelibs_enum_base.EnumBase instead") - def lookup_key(cls, enum_key: str): - """Lookup from key side (must be string)""" - # if there is a ":", then this is legacy, replace with ___ - if ":" in enum_key: - enum_key = enum_key.replace(':', '___') - try: - return cls[enum_key.upper()] - except KeyError as e: - raise ValueError(f"Invalid key: {enum_key}") from e - except AttributeError as e: - raise ValueError(f"Invalid key: {enum_key}") from e - @classmethod - @deprecated("Use corelibs_enum_base.EnumBase instead") - def lookup_value(cls, enum_value: Any): - """Lookup through value side""" - try: - return cls(enum_value) - except ValueError as e: - raise ValueError(f"Invalid value: {enum_value}") from e +# At the module level, issue a deprecation warning +warnings.warn("Use corelibs_enum_base.EnumBase instead", DeprecationWarning, stacklevel=2) - @classmethod - @deprecated("Use corelibs_enum_base.EnumBase instead") - def from_any(cls, enum_any: Any): - """ - This only works in the following order - -> class itself, as is - -> str, assume key lookup - -> if failed try other - - Arguments: - enum_any {Any} -- _description_ - - Returns: - _type_ -- _description_ - """ - if isinstance(enum_any, cls): - return enum_any - # try key first if it is string - # if failed try value - if isinstance(enum_any, str): - try: - return cls.lookup_key(enum_any) - except (ValueError, AttributeError): - try: - return cls.lookup_value(enum_any) - except ValueError as e: - raise ValueError(f"Could not find as key or value: {enum_any}") from e - return cls.lookup_value(enum_any) - - @deprecated("Use corelibs_enum_base.EnumBase instead") - def to_value(self) -> Any: - """Convert to value""" - return self.value - - @deprecated("Use corelibs_enum_base.EnumBase instead") - def to_lower_case(self) -> str: - """return lower case""" - return self.name.lower() - - @deprecated("Use corelibs_enum_base.EnumBase instead") - def __str__(self) -> str: - """return [Enum].NAME like it was called with .name""" - return self.name +# __EMD__ diff --git a/src/corelibs/var_handling/enum_base.pyi b/src/corelibs/var_handling/enum_base.pyi new file mode 100644 index 0000000..4d00370 --- /dev/null +++ b/src/corelibs/var_handling/enum_base.pyi @@ -0,0 +1,15 @@ +""" +Enum base classes [STPUB] +""" + +from typing_extensions import deprecated +from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase + + +@deprecated("Use corelibs_enum_base.EnumBase instead") +class EnumBase(CorelibsEnumBase): + """ + base for enum + lookup_any and from_any will return "EnumBase" and the sub class name + run the return again to "from_any" to get a clean value, or cast it + """ diff --git a/tests/unit/string_handling/test_text_colors.py b/tests/unit/string_handling/test_text_colors.py deleted file mode 100644 index f589c0e..0000000 --- a/tests/unit/string_handling/test_text_colors.py +++ /dev/null @@ -1,516 +0,0 @@ -""" -PyTest: string_handling/text_colors -""" - -import pytest -from corelibs.string_handling.text_colors import Colors - - -class TestColorsInitialState: - """Tests for Colors class initial state""" - - def test_bold_initial_value(self): - """Test that bold has correct ANSI code""" - assert Colors.bold == '\033[1m' - - def test_underline_initial_value(self): - """Test that underline has correct ANSI code""" - assert Colors.underline == '\033[4m' - - def test_end_initial_value(self): - """Test that end has correct ANSI code""" - assert Colors.end == '\033[0m' - - def test_reset_initial_value(self): - """Test that reset has correct ANSI code""" - assert Colors.reset == '\033[0m' - - -class TestColorsNormal: - """Tests for normal color ANSI codes""" - - def test_black_normal(self): - """Test black color code""" - assert Colors.black == "\033[30m" - - def test_red_normal(self): - """Test red color code""" - assert Colors.red == "\033[31m" - - def test_green_normal(self): - """Test green color code""" - assert Colors.green == "\033[32m" - - def test_yellow_normal(self): - """Test yellow color code""" - assert Colors.yellow == "\033[33m" - - def test_blue_normal(self): - """Test blue color code""" - assert Colors.blue == "\033[34m" - - def test_magenta_normal(self): - """Test magenta color code""" - assert Colors.magenta == "\033[35m" - - def test_cyan_normal(self): - """Test cyan color code""" - assert Colors.cyan == "\033[36m" - - def test_white_normal(self): - """Test white color code""" - assert Colors.white == "\033[37m" - - -class TestColorsBold: - """Tests for bold color ANSI codes""" - - def test_black_bold(self): - """Test black bold color code""" - assert Colors.black_bold == "\033[1;30m" - - def test_red_bold(self): - """Test red bold color code""" - assert Colors.red_bold == "\033[1;31m" - - def test_green_bold(self): - """Test green bold color code""" - assert Colors.green_bold == "\033[1;32m" - - def test_yellow_bold(self): - """Test yellow bold color code""" - assert Colors.yellow_bold == "\033[1;33m" - - def test_blue_bold(self): - """Test blue bold color code""" - assert Colors.blue_bold == "\033[1;34m" - - def test_magenta_bold(self): - """Test magenta bold color code""" - assert Colors.magenta_bold == "\033[1;35m" - - def test_cyan_bold(self): - """Test cyan bold color code""" - assert Colors.cyan_bold == "\033[1;36m" - - def test_white_bold(self): - """Test white bold color code""" - assert Colors.white_bold == "\033[1;37m" - - -class TestColorsBright: - """Tests for bright color ANSI codes""" - - def test_black_bright(self): - """Test black bright color code""" - assert Colors.black_bright == '\033[90m' - - def test_red_bright(self): - """Test red bright color code""" - assert Colors.red_bright == '\033[91m' - - def test_green_bright(self): - """Test green bright color code""" - assert Colors.green_bright == '\033[92m' - - def test_yellow_bright(self): - """Test yellow bright color code""" - assert Colors.yellow_bright == '\033[93m' - - def test_blue_bright(self): - """Test blue bright color code""" - assert Colors.blue_bright == '\033[94m' - - def test_magenta_bright(self): - """Test magenta bright color code""" - assert Colors.magenta_bright == '\033[95m' - - def test_cyan_bright(self): - """Test cyan bright color code""" - assert Colors.cyan_bright == '\033[96m' - - def test_white_bright(self): - """Test white bright color code""" - assert Colors.white_bright == '\033[97m' - - -class TestColorsDisable: - """Tests for Colors.disable() method""" - - def setup_method(self): - """Reset colors before each test""" - Colors.reset_colors() - - def teardown_method(self): - """Reset colors after each test""" - Colors.reset_colors() - - def test_disable_bold_and_underline(self): - """Test that disable() sets bold and underline to empty strings""" - Colors.disable() - assert Colors.bold == '' - assert Colors.underline == '' - - def test_disable_end_and_reset(self): - """Test that disable() sets end and reset to empty strings""" - Colors.disable() - assert Colors.end == '' - assert Colors.reset == '' - - def test_disable_normal_colors(self): - """Test that disable() sets all normal colors to empty strings""" - Colors.disable() - assert Colors.black == '' - assert Colors.red == '' - assert Colors.green == '' - assert Colors.yellow == '' - assert Colors.blue == '' - assert Colors.magenta == '' - assert Colors.cyan == '' - assert Colors.white == '' - - def test_disable_bold_colors(self): - """Test that disable() sets all bold colors to empty strings""" - Colors.disable() - assert Colors.black_bold == '' - assert Colors.red_bold == '' - assert Colors.green_bold == '' - assert Colors.yellow_bold == '' - assert Colors.blue_bold == '' - assert Colors.magenta_bold == '' - assert Colors.cyan_bold == '' - assert Colors.white_bold == '' - - def test_disable_bright_colors(self): - """Test that disable() sets all bright colors to empty strings""" - Colors.disable() - assert Colors.black_bright == '' - assert Colors.red_bright == '' - assert Colors.green_bright == '' - assert Colors.yellow_bright == '' - assert Colors.blue_bright == '' - assert Colors.magenta_bright == '' - assert Colors.cyan_bright == '' - assert Colors.white_bright == '' - - def test_disable_all_colors_at_once(self): - """Test that all color attributes are empty after disable()""" - Colors.disable() - # Check that all public attributes are empty strings - for attr in dir(Colors): - if not attr.startswith('_') and attr not in ['disable', 'reset_colors']: - assert getattr(Colors, attr) == '', f"{attr} should be empty after disable()" - - -class TestColorsResetColors: - """Tests for Colors.reset_colors() method""" - - def setup_method(self): - """Disable colors before each test""" - Colors.disable() - - def teardown_method(self): - """Reset colors after each test""" - Colors.reset_colors() - - def test_reset_bold_and_underline(self): - """Test that reset_colors() restores bold and underline""" - Colors.reset_colors() - assert Colors.bold == '\033[1m' - assert Colors.underline == '\033[4m' - - def test_reset_end_and_reset(self): - """Test that reset_colors() restores end and reset""" - Colors.reset_colors() - assert Colors.end == '\033[0m' - assert Colors.reset == '\033[0m' - - def test_reset_normal_colors(self): - """Test that reset_colors() restores all normal colors""" - Colors.reset_colors() - assert Colors.black == "\033[30m" - assert Colors.red == "\033[31m" - assert Colors.green == "\033[32m" - assert Colors.yellow == "\033[33m" - assert Colors.blue == "\033[34m" - assert Colors.magenta == "\033[35m" - assert Colors.cyan == "\033[36m" - assert Colors.white == "\033[37m" - - def test_reset_bold_colors(self): - """Test that reset_colors() restores all bold colors""" - Colors.reset_colors() - assert Colors.black_bold == "\033[1;30m" - assert Colors.red_bold == "\033[1;31m" - assert Colors.green_bold == "\033[1;32m" - assert Colors.yellow_bold == "\033[1;33m" - assert Colors.blue_bold == "\033[1;34m" - assert Colors.magenta_bold == "\033[1;35m" - assert Colors.cyan_bold == "\033[1;36m" - assert Colors.white_bold == "\033[1;37m" - - def test_reset_bright_colors(self): - """Test that reset_colors() restores all bright colors""" - Colors.reset_colors() - assert Colors.black_bright == '\033[90m' - assert Colors.red_bright == '\033[91m' - assert Colors.green_bright == '\033[92m' - assert Colors.yellow_bright == '\033[93m' - assert Colors.blue_bright == '\033[94m' - assert Colors.magenta_bright == '\033[95m' - assert Colors.cyan_bright == '\033[96m' - assert Colors.white_bright == '\033[97m' - - -class TestColorsDisableAndReset: - """Tests for disable and reset cycle""" - - def setup_method(self): - """Reset colors before each test""" - Colors.reset_colors() - - def teardown_method(self): - """Reset colors after each test""" - Colors.reset_colors() - - def test_disable_then_reset_cycle(self): - """Test that colors can be disabled and then reset multiple times""" - # Initial state - original_red = Colors.red - - # Disable - Colors.disable() - assert Colors.red == '' - - # Reset - Colors.reset_colors() - assert Colors.red == original_red - - # Disable again - Colors.disable() - assert Colors.red == '' - - # Reset again - Colors.reset_colors() - assert Colors.red == original_red - - def test_multiple_disables(self): - """Test that calling disable() multiple times is safe""" - Colors.disable() - Colors.disable() - Colors.disable() - assert Colors.red == '' - assert Colors.blue == '' - - def test_multiple_resets(self): - """Test that calling reset_colors() multiple times is safe""" - Colors.reset_colors() - Colors.reset_colors() - Colors.reset_colors() - assert Colors.red == "\033[31m" - assert Colors.blue == "\033[34m" - - -class TestColorsUsage: - """Tests for practical usage of Colors class""" - - def setup_method(self): - """Reset colors before each test""" - Colors.reset_colors() - - def teardown_method(self): - """Reset colors after each test""" - Colors.reset_colors() - - def test_colored_string_with_reset(self): - """Test creating a colored string with reset""" - result = f"{Colors.red}Error{Colors.end}" - assert result == "\033[31mError\033[0m" - - def test_bold_colored_string(self): - """Test creating a bold colored string""" - result = f"{Colors.bold}{Colors.yellow}Warning{Colors.end}" - assert result == "\033[1m\033[33mWarning\033[0m" - - def test_underline_colored_string(self): - """Test creating an underlined colored string""" - result = f"{Colors.underline}{Colors.blue}Info{Colors.end}" - assert result == "\033[4m\033[34mInfo\033[0m" - - def test_bold_underline_colored_string(self): - """Test creating a bold and underlined colored string""" - result = f"{Colors.bold}{Colors.underline}{Colors.green}Success{Colors.end}" - assert result == "\033[1m\033[4m\033[32mSuccess\033[0m" - - def test_multiple_colors_in_string(self): - """Test using multiple colors in one string""" - result = f"{Colors.red}Red{Colors.end} {Colors.blue}Blue{Colors.end}" - assert result == "\033[31mRed\033[0m \033[34mBlue\033[0m" - - def test_bright_color_usage(self): - """Test using bright color variants""" - result = f"{Colors.cyan_bright}Bright Cyan{Colors.end}" - assert result == "\033[96mBright Cyan\033[0m" - - def test_bold_color_shortcut(self): - """Test using bold color shortcuts""" - result = f"{Colors.red_bold}Bold Red{Colors.end}" - assert result == "\033[1;31mBold Red\033[0m" - - def test_disabled_colors_produce_plain_text(self): - """Test that disabled colors produce plain text without ANSI codes""" - Colors.disable() - result = f"{Colors.red}Error{Colors.end}" - assert result == "Error" - assert "\033[" not in result - - def test_disabled_bold_underline_produce_plain_text(self): - """Test that disabled formatting produces plain text""" - Colors.disable() - result = f"{Colors.bold}{Colors.underline}{Colors.green}Success{Colors.end}" - assert result == "Success" - assert "\033[" not in result - - -class TestColorsPrivateAttributes: - """Tests to ensure private attributes are not directly accessible""" - - def test_private_bold_not_accessible(self): - """Test that __BOLD is private""" - with pytest.raises(AttributeError): - _ = Colors.__BOLD - - def test_private_colors_not_accessible(self): - """Test that private color attributes are not accessible""" - with pytest.raises(AttributeError): - _ = Colors.__RED - with pytest.raises(AttributeError): - _ = Colors.__GREEN - - -# Parametrized tests -@pytest.mark.parametrize("color_attr,expected_code", [ - ("black", "\033[30m"), - ("red", "\033[31m"), - ("green", "\033[32m"), - ("yellow", "\033[33m"), - ("blue", "\033[34m"), - ("magenta", "\033[35m"), - ("cyan", "\033[36m"), - ("white", "\033[37m"), -]) -def test_normal_colors_parametrized(color_attr: str, expected_code: str): - """Parametrized test for normal colors""" - Colors.reset_colors() - assert getattr(Colors, color_attr) == expected_code - - -@pytest.mark.parametrize("color_attr,expected_code", [ - ("black_bold", "\033[1;30m"), - ("red_bold", "\033[1;31m"), - ("green_bold", "\033[1;32m"), - ("yellow_bold", "\033[1;33m"), - ("blue_bold", "\033[1;34m"), - ("magenta_bold", "\033[1;35m"), - ("cyan_bold", "\033[1;36m"), - ("white_bold", "\033[1;37m"), -]) -def test_bold_colors_parametrized(color_attr: str, expected_code: str): - """Parametrized test for bold colors""" - Colors.reset_colors() - assert getattr(Colors, color_attr) == expected_code - - -@pytest.mark.parametrize("color_attr,expected_code", [ - ("black_bright", '\033[90m'), - ("red_bright", '\033[91m'), - ("green_bright", '\033[92m'), - ("yellow_bright", '\033[93m'), - ("blue_bright", '\033[94m'), - ("magenta_bright", '\033[95m'), - ("cyan_bright", '\033[96m'), - ("white_bright", '\033[97m'), -]) -def test_bright_colors_parametrized(color_attr: str, expected_code: str): - """Parametrized test for bright colors""" - Colors.reset_colors() - assert getattr(Colors, color_attr) == expected_code - - -@pytest.mark.parametrize("color_attr", [ - "bold", "underline", "end", "reset", - "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", - "black_bold", "red_bold", "green_bold", "yellow_bold", - "blue_bold", "magenta_bold", "cyan_bold", "white_bold", - "black_bright", "red_bright", "green_bright", "yellow_bright", - "blue_bright", "magenta_bright", "cyan_bright", "white_bright", -]) -def test_disable_all_attributes_parametrized(color_attr: str): - """Parametrized test that all color attributes are disabled""" - Colors.reset_colors() - Colors.disable() - assert getattr(Colors, color_attr) == '' - - -@pytest.mark.parametrize("color_attr", [ - "bold", "underline", "end", "reset", - "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", - "black_bold", "red_bold", "green_bold", "yellow_bold", - "blue_bold", "magenta_bold", "cyan_bold", "white_bold", - "black_bright", "red_bright", "green_bright", "yellow_bright", - "blue_bright", "magenta_bright", "cyan_bright", "white_bright", -]) -def test_reset_all_attributes_parametrized(color_attr: str): - """Parametrized test that all color attributes are reset""" - Colors.disable() - Colors.reset_colors() - assert getattr(Colors, color_attr) != '' - assert '\033[' in getattr(Colors, color_attr) - - -# Edge case tests -class TestColorsEdgeCases: - """Tests for edge cases and special scenarios""" - - def setup_method(self): - """Reset colors before each test""" - Colors.reset_colors() - - def teardown_method(self): - """Reset colors after each test""" - Colors.reset_colors() - - def test_colors_class_is_not_instantiable(self): - """Test that Colors class can be instantiated (it's not abstract)""" - # The class uses static methods, but can be instantiated - instance = Colors() - assert isinstance(instance, Colors) - - def test_static_methods_work_on_instance(self): - """Test that static methods work when called on instance""" - instance = Colors() - instance.disable() - assert Colors.red == '' - instance.reset_colors() - assert Colors.red == "\033[31m" - - def test_concatenation_of_multiple_effects(self): - """Test concatenating multiple color effects""" - result = f"{Colors.bold}{Colors.underline}{Colors.red_bright}Test{Colors.reset}" - assert "\033[1m" in result # bold - assert "\033[4m" in result # underline - assert "\033[91m" in result # red bright - assert "\033[0m" in result # reset - - def test_empty_string_with_colors(self): - """Test applying colors to empty string""" - result = f"{Colors.red}{Colors.end}" - assert result == "\033[31m\033[0m" - - def test_nested_color_changes(self): - """Test nested color changes in string""" - result = f"{Colors.red}Red {Colors.blue}Blue{Colors.end} Red again{Colors.end}" - assert result == "\033[31mRed \033[34mBlue\033[0m Red again\033[0m" - - -# __END__ diff --git a/uv.lock b/uv.lock index abe6b50..1a7c1a2 100644 --- a/uv.lock +++ b/uv.lock @@ -113,6 +113,7 @@ source = { editable = "." } dependencies = [ { name = "corelibs-datetime" }, { name = "corelibs-enum-base" }, + { name = "corelibs-text-colors" }, { name = "corelibs-var" }, { name = "cryptography" }, { name = "jmespath" }, @@ -126,12 +127,14 @@ dev = [ { name = "deepdiff" }, { name = "pytest" }, { name = "pytest-cov" }, + { name = "typing-extensions" }, ] [package.metadata] requires-dist = [ { name = "corelibs-datetime", specifier = ">=1.0.1", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }, { name = "corelibs-enum-base", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }, + { name = "corelibs-text-colors", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }, { name = "corelibs-var", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }, { name = "cryptography", specifier = ">=46.0.3" }, { name = "jmespath", specifier = ">=1.0.1" }, @@ -145,6 +148,7 @@ dev = [ { name = "deepdiff", specifier = ">=8.6.1" }, { name = "pytest", specifier = ">=8.4.1" }, { name = "pytest-cov", specifier = ">=6.2.1" }, + { name = "typing-extensions", specifier = ">=4.15.0" }, ] [[package]] @@ -168,6 +172,15 @@ wheels = [ { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-enum-base/1.0.0/corelibs_enum_base-1.0.0-py3-none-any.whl", hash = "sha256:c305d4063c69021aaf9ef75fbcce961039dae3c3de7820febeac7082c998a1f8" }, ] +[[package]] +name = "corelibs-text-colors" +version = "1.0.0" +source = { registry = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" } +sdist = { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-text-colors/1.0.0/corelibs_text_colors-1.0.0.tar.gz", hash = "sha256:482fee54eb94d6b8acff091b531213ea312fac866836951dc06b26994fa52161" } +wheels = [ + { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-text-colors/1.0.0/corelibs_text_colors-1.0.0-py3-none-any.whl", hash = "sha256:ed0ba1799ce437e3fec67c94f0d9adfddde26094143d6a019e05d7557fecb03c" }, +] + [[package]] name = "corelibs-var" version = "1.0.0" @@ -470,6 +483,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + [[package]] name = "urllib3" version = "2.5.0"