Remove text color handling from corelibs and use corelibs_text_colors instead
Also update enum with proper pyi file for deprecation warnings
This commit is contained in:
@@ -8,6 +8,7 @@ requires-python = ">=3.13"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"corelibs-datetime>=1.0.1",
|
"corelibs-datetime>=1.0.1",
|
||||||
"corelibs-enum-base>=1.0.0",
|
"corelibs-enum-base>=1.0.0",
|
||||||
|
"corelibs-text-colors>=1.0.0",
|
||||||
"corelibs-var>=1.0.0",
|
"corelibs-var>=1.0.0",
|
||||||
"cryptography>=46.0.3",
|
"cryptography>=46.0.3",
|
||||||
"jmespath>=1.0.1",
|
"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-enum-base = { index = "opj-pypi" }
|
||||||
corelibs-datetime = { index = "opj-pypi" }
|
corelibs-datetime = { index = "opj-pypi" }
|
||||||
corelibs-var = { index = "opj-pypi" }
|
corelibs-var = { index = "opj-pypi" }
|
||||||
|
corelibs-text-colors = { index = "opj-pypi" }
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"deepdiff>=8.6.1",
|
"deepdiff>=8.6.1",
|
||||||
"pytest>=8.4.1",
|
"pytest>=8.4.1",
|
||||||
"pytest-cov>=6.2.1",
|
"pytest-cov>=6.2.1",
|
||||||
|
"typing-extensions>=4.15.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
# MARK: Python linting
|
# MARK: Python linting
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ from pathlib import Path
|
|||||||
import atexit
|
import atexit
|
||||||
from enum import Flag, auto
|
from enum import Flag, auto
|
||||||
from typing import MutableMapping, TextIO, TypedDict, Any, TYPE_CHECKING, cast
|
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.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
|
from corelibs.debug_handling.debug_helpers import call_stack, exception_stack
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
@@ -5,152 +5,14 @@ Set colors with print(f"something {Colors.yellow}colorful{Colors.end})
|
|||||||
bold + underline + color combinations are possible.
|
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
|
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__
|
# __END__
|
||||||
|
|||||||
@@ -2,82 +2,24 @@
|
|||||||
Enum base classes
|
Enum base classes
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from enum import Enum
|
import warnings
|
||||||
from warnings import deprecated
|
from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase
|
||||||
from typing import Any
|
|
||||||
# from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase
|
|
||||||
|
|
||||||
|
|
||||||
class EnumBase(Enum):
|
class EnumBase(CorelibsEnumBase):
|
||||||
"""
|
"""
|
||||||
base for enum
|
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
|
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
|
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
|
# At the module level, issue a deprecation warning
|
||||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
warnings.warn("Use corelibs_enum_base.EnumBase instead", DeprecationWarning, stacklevel=2)
|
||||||
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
|
|
||||||
|
|
||||||
@classmethod
|
# __EMD__
|
||||||
@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
|
|
||||||
|
|||||||
15
src/corelibs/var_handling/enum_base.pyi
Normal file
15
src/corelibs/var_handling/enum_base.pyi
Normal file
@@ -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
|
||||||
|
"""
|
||||||
@@ -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__
|
|
||||||
22
uv.lock
generated
22
uv.lock
generated
@@ -113,6 +113,7 @@ source = { editable = "." }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "corelibs-datetime" },
|
{ name = "corelibs-datetime" },
|
||||||
{ name = "corelibs-enum-base" },
|
{ name = "corelibs-enum-base" },
|
||||||
|
{ name = "corelibs-text-colors" },
|
||||||
{ name = "corelibs-var" },
|
{ name = "corelibs-var" },
|
||||||
{ name = "cryptography" },
|
{ name = "cryptography" },
|
||||||
{ name = "jmespath" },
|
{ name = "jmespath" },
|
||||||
@@ -126,12 +127,14 @@ dev = [
|
|||||||
{ name = "deepdiff" },
|
{ name = "deepdiff" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-cov" },
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "corelibs-datetime", specifier = ">=1.0.1", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" },
|
{ 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-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 = "corelibs-var", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" },
|
||||||
{ name = "cryptography", specifier = ">=46.0.3" },
|
{ name = "cryptography", specifier = ">=46.0.3" },
|
||||||
{ name = "jmespath", specifier = ">=1.0.1" },
|
{ name = "jmespath", specifier = ">=1.0.1" },
|
||||||
@@ -145,6 +148,7 @@ dev = [
|
|||||||
{ name = "deepdiff", specifier = ">=8.6.1" },
|
{ name = "deepdiff", specifier = ">=8.6.1" },
|
||||||
{ name = "pytest", specifier = ">=8.4.1" },
|
{ name = "pytest", specifier = ">=8.4.1" },
|
||||||
{ name = "pytest-cov", specifier = ">=6.2.1" },
|
{ name = "pytest-cov", specifier = ">=6.2.1" },
|
||||||
|
{ name = "typing-extensions", specifier = ">=4.15.0" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "corelibs-var"
|
name = "corelibs-var"
|
||||||
version = "1.0.0"
|
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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user