xulbux 1.7.0__py3-none-any.whl → 1.7.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xulbux might be problematic. Click here for more details.
- xulbux/__init__.py +1 -1
- xulbux/_consts_.py +2 -4
- xulbux/xx_code.py +1 -1
- xulbux/xx_color.py +45 -33
- xulbux/xx_console.py +151 -52
- xulbux/xx_format_codes.py +66 -36
- xulbux/xx_regex.py +9 -9
- {xulbux-1.7.0.dist-info → xulbux-1.7.2.dist-info}/METADATA +36 -34
- xulbux-1.7.2.dist-info/RECORD +21 -0
- xulbux-1.7.0.dist-info/RECORD +0 -21
- {xulbux-1.7.0.dist-info → xulbux-1.7.2.dist-info}/WHEEL +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.2.dist-info}/entry_points.txt +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.2.dist-info}/licenses/LICENSE +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.2.dist-info}/top_level.txt +0 -0
xulbux/__init__.py
CHANGED
xulbux/_consts_.py
CHANGED
|
@@ -94,8 +94,6 @@ class ANSI:
|
|
|
94
94
|
"""The separator between ANSI escape sequence parts."""
|
|
95
95
|
END = end = "m"
|
|
96
96
|
"""The end of an ANSI escape sequence."""
|
|
97
|
-
default_color_modifiers: dict[str, str] = {"lighten": "+l", "darken": "-d"}
|
|
98
|
-
"""Characters to modify the lightness of the default color with."""
|
|
99
97
|
|
|
100
98
|
@classmethod
|
|
101
99
|
def seq(cls, parts: int = 1) -> FormattableString:
|
|
@@ -107,7 +105,7 @@ class ANSI:
|
|
|
107
105
|
seq_bg_color: FormattableString = CHAR + START + "48" + SEP + "2" + SEP + "{}" + SEP + "{}" + SEP + "{}" + END
|
|
108
106
|
"""The ANSI escape sequence for setting the background RGB color."""
|
|
109
107
|
|
|
110
|
-
color_map:
|
|
108
|
+
color_map: tuple[str, ...] = (
|
|
111
109
|
########### DEFAULT CONSOLE COLOR NAMES ############
|
|
112
110
|
"black",
|
|
113
111
|
"red",
|
|
@@ -117,7 +115,7 @@ class ANSI:
|
|
|
117
115
|
"magenta",
|
|
118
116
|
"cyan",
|
|
119
117
|
"white",
|
|
120
|
-
|
|
118
|
+
)
|
|
121
119
|
"""The console default color names."""
|
|
122
120
|
|
|
123
121
|
codes_map: dict[str | tuple[str, ...], int] = {
|
xulbux/xx_code.py
CHANGED
|
@@ -53,7 +53,7 @@ class Code:
|
|
|
53
53
|
return list(Data.remove_duplicates(funcs + nested_func_calls))
|
|
54
54
|
|
|
55
55
|
@staticmethod
|
|
56
|
-
def is_js(code: str, funcs: list = ["__", "$t", "$lang"]) -> bool:
|
|
56
|
+
def is_js(code: str, funcs: list[str] = ["__", "$t", "$lang"]) -> bool:
|
|
57
57
|
"""Will check if the code is very likely to be JavaScript."""
|
|
58
58
|
if not code or len(code.strip()) < 3:
|
|
59
59
|
return False
|
xulbux/xx_color.py
CHANGED
|
@@ -33,7 +33,7 @@ The `Color` class, which contains all sorts of different color-related methods:
|
|
|
33
33
|
|
|
34
34
|
from .xx_regex import Regex
|
|
35
35
|
|
|
36
|
-
from typing import Annotated, TypeAlias, Iterator, Optional, Literal, Union
|
|
36
|
+
from typing import Annotated, TypeAlias, Iterator, Optional, Literal, Union, Any, cast
|
|
37
37
|
import re as _re
|
|
38
38
|
|
|
39
39
|
|
|
@@ -42,6 +42,10 @@ Int_0_255 = Annotated[int, "An integer value between 0 and 255, inclusive."]
|
|
|
42
42
|
Int_0_360 = Annotated[int, "An integer value between 0 and 360, inclusive."]
|
|
43
43
|
Float_0_1 = Annotated[float, "A float value between 0.0 and 1.0, inclusive."]
|
|
44
44
|
|
|
45
|
+
AnyRgba: TypeAlias = Any
|
|
46
|
+
AnyHsla: TypeAlias = Any
|
|
47
|
+
AnyHexa: TypeAlias = Any
|
|
48
|
+
|
|
45
49
|
Rgba: TypeAlias = Union[
|
|
46
50
|
tuple[Int_0_255, Int_0_255, Int_0_255],
|
|
47
51
|
tuple[Int_0_255, Int_0_255, Int_0_255, Float_0_1],
|
|
@@ -178,7 +182,7 @@ class rgba:
|
|
|
178
182
|
self.a = 1 - self.a
|
|
179
183
|
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
180
184
|
|
|
181
|
-
def grayscale(self, method:
|
|
185
|
+
def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "rgba":
|
|
182
186
|
"""Converts the color to grayscale using the luminance formula.\n
|
|
183
187
|
------------------------------------------------------------------
|
|
184
188
|
The `method` is the luminance calculation method to use:
|
|
@@ -387,7 +391,7 @@ class hsla:
|
|
|
387
391
|
self.a = 1 - self.a
|
|
388
392
|
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
389
393
|
|
|
390
|
-
def grayscale(self, method:
|
|
394
|
+
def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "hsla":
|
|
391
395
|
"""Converts the color to grayscale using the luminance formula.\n
|
|
392
396
|
------------------------------------------------------------------
|
|
393
397
|
The `method` is the luminance calculation method to use:
|
|
@@ -496,7 +500,7 @@ class hexa:
|
|
|
496
500
|
self.b: int
|
|
497
501
|
self.a: Optional[float]
|
|
498
502
|
if all(x is not None for x in (_r, _g, _b)):
|
|
499
|
-
self.r, self.g, self.b, self.a = _r, _g, _b, _a
|
|
503
|
+
self.r, self.g, self.b, self.a = cast(int, _r), cast(int, _g), cast(int, _b), _a
|
|
500
504
|
return
|
|
501
505
|
if isinstance(color, hexa):
|
|
502
506
|
raise ValueError("Color is already a hexa() color")
|
|
@@ -628,7 +632,7 @@ class hexa:
|
|
|
628
632
|
self.a = 1 - self.a
|
|
629
633
|
return hexa("", self.r, self.g, self.b, self.a)
|
|
630
634
|
|
|
631
|
-
def grayscale(self, method:
|
|
635
|
+
def grayscale(self, method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2") -> "hexa":
|
|
632
636
|
"""Converts the color to grayscale using the luminance formula.\n
|
|
633
637
|
------------------------------------------------------------------
|
|
634
638
|
The `method` is the luminance calculation method to use:
|
|
@@ -677,7 +681,7 @@ class hexa:
|
|
|
677
681
|
class Color:
|
|
678
682
|
|
|
679
683
|
@staticmethod
|
|
680
|
-
def is_valid_rgba(color:
|
|
684
|
+
def is_valid_rgba(color: AnyRgba, allow_alpha: bool = True) -> bool:
|
|
681
685
|
try:
|
|
682
686
|
if isinstance(color, rgba):
|
|
683
687
|
return True
|
|
@@ -685,7 +689,7 @@ class Color:
|
|
|
685
689
|
if allow_alpha and Color.has_alpha(color):
|
|
686
690
|
return (
|
|
687
691
|
0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
|
|
688
|
-
and (0 <= color[3] <= 1 or color[3] is None)
|
|
692
|
+
and (0 <= color[3] <= 1 or color[3] is None)
|
|
689
693
|
)
|
|
690
694
|
elif len(color) == 3:
|
|
691
695
|
return 0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
|
|
@@ -708,7 +712,7 @@ class Color:
|
|
|
708
712
|
return False
|
|
709
713
|
|
|
710
714
|
@staticmethod
|
|
711
|
-
def is_valid_hsla(color:
|
|
715
|
+
def is_valid_hsla(color: AnyHsla, allow_alpha: bool = True) -> bool:
|
|
712
716
|
try:
|
|
713
717
|
if isinstance(color, hsla):
|
|
714
718
|
return True
|
|
@@ -716,7 +720,7 @@ class Color:
|
|
|
716
720
|
if allow_alpha and Color.has_alpha(color):
|
|
717
721
|
return (
|
|
718
722
|
0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
|
|
719
|
-
and (0 <= color[3] <= 1 or color[3] is None)
|
|
723
|
+
and (0 <= color[3] <= 1 or color[3] is None)
|
|
720
724
|
)
|
|
721
725
|
elif len(color) == 3:
|
|
722
726
|
return 0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
|
|
@@ -734,12 +738,13 @@ class Color:
|
|
|
734
738
|
return False
|
|
735
739
|
elif isinstance(color, str):
|
|
736
740
|
return bool(_re.fullmatch(Regex.hsla_str(allow_alpha=allow_alpha), color))
|
|
741
|
+
return False
|
|
737
742
|
except Exception:
|
|
738
743
|
return False
|
|
739
744
|
|
|
740
745
|
@staticmethod
|
|
741
746
|
def is_valid_hexa(
|
|
742
|
-
color:
|
|
747
|
+
color: AnyHexa,
|
|
743
748
|
allow_alpha: bool = True,
|
|
744
749
|
get_prefix: bool = False,
|
|
745
750
|
) -> bool | tuple[bool, Optional[Literal['#', '0x']]]:
|
|
@@ -754,14 +759,15 @@ class Color:
|
|
|
754
759
|
(color[2:], "0x") if color.startswith("0x") else (color, None))
|
|
755
760
|
return ((bool(_re.fullmatch(Regex.hexa_str(allow_alpha=allow_alpha), color)),
|
|
756
761
|
prefix) if get_prefix else bool(_re.fullmatch(Regex.hexa_str(allow_alpha=allow_alpha), color)))
|
|
762
|
+
return False
|
|
757
763
|
except Exception:
|
|
758
764
|
return (False, None) if get_prefix else False
|
|
759
765
|
|
|
760
766
|
@staticmethod
|
|
761
|
-
def is_valid(color:
|
|
767
|
+
def is_valid(color: AnyRgba | AnyHsla | AnyHexa, allow_alpha: bool = True) -> bool:
|
|
762
768
|
return bool(
|
|
763
|
-
Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha)
|
|
764
|
-
or Color.is_valid_hexa(color, allow_alpha)
|
|
769
|
+
Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha)
|
|
770
|
+
or Color.is_valid_hexa(color, allow_alpha)
|
|
765
771
|
)
|
|
766
772
|
|
|
767
773
|
@staticmethod
|
|
@@ -772,7 +778,7 @@ class Color:
|
|
|
772
778
|
Returns `True` if the color has an alpha channel and `False` otherwise."""
|
|
773
779
|
if isinstance(color, (rgba, hsla, hexa)):
|
|
774
780
|
return color.has_alpha()
|
|
775
|
-
if Color.is_valid_hexa(color):
|
|
781
|
+
if Color.is_valid_hexa(color):
|
|
776
782
|
if isinstance(color, str):
|
|
777
783
|
if color.startswith("#"):
|
|
778
784
|
color = color[1:]
|
|
@@ -791,11 +797,11 @@ class Color:
|
|
|
791
797
|
"""Will try to convert any color type to a color of type RGBA."""
|
|
792
798
|
if isinstance(color, (hsla, hexa)):
|
|
793
799
|
return color.to_rgba()
|
|
794
|
-
elif Color.is_valid_hsla(color):
|
|
800
|
+
elif Color.is_valid_hsla(color):
|
|
795
801
|
return hsla(*color, _validate=False).to_rgba() # type: ignore[not-iterable]
|
|
796
|
-
elif Color.is_valid_hexa(color):
|
|
797
|
-
return hexa(color).to_rgba()
|
|
798
|
-
elif Color.is_valid_rgba(color):
|
|
802
|
+
elif Color.is_valid_hexa(color):
|
|
803
|
+
return hexa(cast(str | int, color)).to_rgba()
|
|
804
|
+
elif Color.is_valid_rgba(color):
|
|
799
805
|
return color if isinstance(color, rgba) else (rgba(*color, _validate=False)) # type: ignore[not-iterable]
|
|
800
806
|
raise ValueError(f"Invalid color format '{color}'")
|
|
801
807
|
|
|
@@ -804,11 +810,11 @@ class Color:
|
|
|
804
810
|
"""Will try to convert any color type to a color of type HSLA."""
|
|
805
811
|
if isinstance(color, (rgba, hexa)):
|
|
806
812
|
return color.to_hsla()
|
|
807
|
-
elif Color.is_valid_rgba(color):
|
|
813
|
+
elif Color.is_valid_rgba(color):
|
|
808
814
|
return rgba(*color, _validate=False).to_hsla() # type: ignore[not-iterable]
|
|
809
|
-
elif Color.is_valid_hexa(color):
|
|
810
|
-
return hexa(color).to_hsla()
|
|
811
|
-
elif Color.is_valid_hsla(color):
|
|
815
|
+
elif Color.is_valid_hexa(color):
|
|
816
|
+
return hexa(cast(str | int, color)).to_hsla()
|
|
817
|
+
elif Color.is_valid_hsla(color):
|
|
812
818
|
return color if isinstance(color, hsla) else (hsla(*color, _validate=False)) # type: ignore[not-iterable]
|
|
813
819
|
raise ValueError(f"Invalid color format '{color}'")
|
|
814
820
|
|
|
@@ -817,12 +823,12 @@ class Color:
|
|
|
817
823
|
"""Will try to convert any color type to a color of type HEXA."""
|
|
818
824
|
if isinstance(color, (rgba, hsla)):
|
|
819
825
|
return color.to_hexa()
|
|
820
|
-
elif Color.is_valid_rgba(color):
|
|
826
|
+
elif Color.is_valid_rgba(color):
|
|
821
827
|
return rgba(*color, _validate=False).to_hexa() # type: ignore[not-iterable]
|
|
822
|
-
elif Color.is_valid_hsla(color):
|
|
828
|
+
elif Color.is_valid_hsla(color):
|
|
823
829
|
return hsla(*color, _validate=False).to_hexa() # type: ignore[not-iterable]
|
|
824
|
-
elif Color.is_valid_hexa(color):
|
|
825
|
-
return color if isinstance(color, hexa) else hexa(color)
|
|
830
|
+
elif Color.is_valid_hexa(color):
|
|
831
|
+
return color if isinstance(color, hexa) else hexa(cast(str | int, color))
|
|
826
832
|
raise ValueError(f"Invalid color format '{color}'")
|
|
827
833
|
|
|
828
834
|
@staticmethod
|
|
@@ -919,7 +925,13 @@ class Color:
|
|
|
919
925
|
raise ValueError(f"Invalid HEX integer '0x{hex_str}': expected in range [0x000000, 0xFFFFFF]")
|
|
920
926
|
|
|
921
927
|
@staticmethod
|
|
922
|
-
def luminance(
|
|
928
|
+
def luminance(
|
|
929
|
+
r: int,
|
|
930
|
+
g: int,
|
|
931
|
+
b: int,
|
|
932
|
+
output_type: Optional[type] = None,
|
|
933
|
+
method: Literal["wcag2", "wcag3", "simple", "bt601"] = "wcag2",
|
|
934
|
+
) -> int | float:
|
|
923
935
|
"""Calculates the relative luminance of a color according to various standards.\n
|
|
924
936
|
----------------------------------------------------------------------------------
|
|
925
937
|
The `output_type` controls the range of the returned luminance value:
|
|
@@ -963,7 +975,7 @@ class Color:
|
|
|
963
975
|
|
|
964
976
|
@staticmethod
|
|
965
977
|
def text_color_for_on_bg(text_bg_color: Rgba | Hexa) -> rgba | hexa | int:
|
|
966
|
-
was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int)
|
|
978
|
+
was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int)
|
|
967
979
|
text_bg_color = Color.to_rgba(text_bg_color)
|
|
968
980
|
brightness = 0.2126 * text_bg_color[0] + 0.7152 * text_bg_color[1] + 0.0722 * text_bg_color[2]
|
|
969
981
|
return (((0xFFFFFF if was_int else hexa("", 255, 255, 255)) if was_hexa else rgba(255, 255, 255, _validate=False))
|
|
@@ -978,22 +990,22 @@ class Color:
|
|
|
978
990
|
- lightness_change (float): float between -1.0 (darken by `100%`) and 1.0 (lighten by `100%`)\n
|
|
979
991
|
-----------------------------------------------------------------------------------------------------
|
|
980
992
|
returns (rgba|hexa): the adjusted color in the format of the input color"""
|
|
981
|
-
was_hexa = Color.is_valid_hexa(color)
|
|
982
|
-
_color: hsla = Color.to_hsla(color)
|
|
993
|
+
was_hexa = Color.is_valid_hexa(color)
|
|
994
|
+
_color: hsla = Color.to_hsla(color)
|
|
983
995
|
h, s, l, a = (int(_color[0]), int(_color[1]), int(_color[2]), _color[3] if Color.has_alpha(_color) else None)
|
|
984
996
|
l = int(max(0, min(100, l + lightness_change * 100)))
|
|
985
997
|
return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
|
|
986
998
|
|
|
987
999
|
@staticmethod
|
|
988
|
-
def adjust_saturation(color: Rgba |
|
|
1000
|
+
def adjust_saturation(color: Rgba | Hexa, saturation_change: float) -> rgba | hexa:
|
|
989
1001
|
"""In- or decrease the saturation of the input color.\n
|
|
990
1002
|
-----------------------------------------------------------------------------------------------------------
|
|
991
1003
|
- color (rgba|hexa): HEX or RGBA color
|
|
992
1004
|
- saturation_change (float): float between -1.0 (saturate by `100%`) and 1.0 (desaturate by `100%`)\n
|
|
993
1005
|
-----------------------------------------------------------------------------------------------------------
|
|
994
1006
|
returns (rgba|hexa): the adjusted color in the format of the input color"""
|
|
995
|
-
was_hexa = Color.is_valid_hexa(color)
|
|
996
|
-
_color: hsla = Color.to_hsla(color)
|
|
1007
|
+
was_hexa = Color.is_valid_hexa(color)
|
|
1008
|
+
_color: hsla = Color.to_hsla(color)
|
|
997
1009
|
h, s, l, a = (int(_color[0]), int(_color[1]), int(_color[2]), _color[3] if Color.has_alpha(_color) else None)
|
|
998
1010
|
s = int(max(0, min(100, s + saturation_change * 100)))
|
|
999
1011
|
return hsla(h, s, l, a, _validate=False).to_hexa() if was_hexa else hsla(h, s, l, a, _validate=False).to_rgba()
|
xulbux/xx_console.py
CHANGED
|
@@ -11,7 +11,7 @@ from .xx_string import String
|
|
|
11
11
|
from .xx_color import Color, Rgba, Hexa
|
|
12
12
|
|
|
13
13
|
from prompt_toolkit.key_binding.key_bindings import KeyBindings
|
|
14
|
-
from typing import Optional, Any
|
|
14
|
+
from typing import Optional, Literal, Mapping, Any, cast
|
|
15
15
|
import prompt_toolkit as _prompt_toolkit
|
|
16
16
|
import pyperclip as _pyperclip
|
|
17
17
|
import keyboard as _keyboard
|
|
@@ -25,20 +25,29 @@ import os as _os
|
|
|
25
25
|
class _ConsoleWidth:
|
|
26
26
|
|
|
27
27
|
def __get__(self, obj, owner=None):
|
|
28
|
-
|
|
28
|
+
try:
|
|
29
|
+
return _os.get_terminal_size().columns
|
|
30
|
+
except OSError:
|
|
31
|
+
return 80
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
class _ConsoleHeight:
|
|
32
35
|
|
|
33
36
|
def __get__(self, obj, owner=None):
|
|
34
|
-
|
|
37
|
+
try:
|
|
38
|
+
return _os.get_terminal_size().lines
|
|
39
|
+
except OSError:
|
|
40
|
+
return 24
|
|
35
41
|
|
|
36
42
|
|
|
37
43
|
class _ConsoleSize:
|
|
38
44
|
|
|
39
45
|
def __get__(self, obj, owner=None):
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
try:
|
|
47
|
+
size = _os.get_terminal_size()
|
|
48
|
+
return (size.columns, size.lines)
|
|
49
|
+
except OSError:
|
|
50
|
+
return (80, 24)
|
|
42
51
|
|
|
43
52
|
|
|
44
53
|
class _ConsoleUser:
|
|
@@ -48,25 +57,31 @@ class _ConsoleUser:
|
|
|
48
57
|
|
|
49
58
|
|
|
50
59
|
class ArgResult:
|
|
51
|
-
"""
|
|
52
|
-
|
|
60
|
+
"""Represents the result of a parsed command-line argument and contains the following attributes:
|
|
61
|
+
- `exists` -⠀if the argument was found or not
|
|
62
|
+
- `value` -⠀the value given with the found argument\n
|
|
63
|
+
--------------------------------------------------------------------------------------------------------
|
|
64
|
+
When the `ArgResult` instance is accessed as a boolean it will correspond to the `exists` attribute."""
|
|
53
65
|
|
|
54
66
|
def __init__(self, exists: bool, value: Any):
|
|
55
|
-
self.exists = exists
|
|
56
|
-
self.value = value
|
|
67
|
+
self.exists: bool = exists
|
|
68
|
+
self.value: Any = value
|
|
57
69
|
|
|
58
70
|
def __bool__(self):
|
|
59
71
|
return self.exists
|
|
60
72
|
|
|
61
73
|
|
|
62
74
|
class Args:
|
|
63
|
-
"""
|
|
75
|
+
"""Container for parsed command-line arguments, allowing attribute-style access.
|
|
76
|
+
For example, if an argument `foo` was parsed, it can be accessed via `args.foo`.
|
|
77
|
+
Each such attribute (e.g. `args.foo`) is an instance of `ArgResult`."""
|
|
64
78
|
|
|
65
|
-
def __init__(self, **kwargs):
|
|
66
|
-
for
|
|
67
|
-
if not
|
|
68
|
-
raise TypeError(f"Argument alias '{
|
|
69
|
-
|
|
79
|
+
def __init__(self, **kwargs: dict[str, Any]):
|
|
80
|
+
for alias_name, data_dict in kwargs.items():
|
|
81
|
+
if not alias_name.isidentifier():
|
|
82
|
+
raise TypeError(f"Argument alias '{alias_name}' is invalid. It must be a valid Python variable name.")
|
|
83
|
+
arg_result_instance = ArgResult(exists=data_dict["exists"], value=data_dict["value"])
|
|
84
|
+
setattr(self, alias_name, arg_result_instance)
|
|
70
85
|
|
|
71
86
|
def __len__(self):
|
|
72
87
|
return len(vars(self))
|
|
@@ -74,6 +89,9 @@ class Args:
|
|
|
74
89
|
def __contains__(self, key):
|
|
75
90
|
return hasattr(self, key)
|
|
76
91
|
|
|
92
|
+
def __getattr__(self, name: str) -> ArgResult:
|
|
93
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
94
|
+
|
|
77
95
|
def __getitem__(self, key):
|
|
78
96
|
if isinstance(key, int):
|
|
79
97
|
return list(self.__iter__())[key]
|
|
@@ -108,14 +126,13 @@ class Console:
|
|
|
108
126
|
h: int = _ConsoleHeight() # type: ignore[assignment]
|
|
109
127
|
"""The height of the console in lines."""
|
|
110
128
|
wh: tuple[int, int] = _ConsoleSize() # type: ignore[assignment]
|
|
111
|
-
"""A tuple with the width and height of
|
|
112
|
-
the console in characters and lines."""
|
|
129
|
+
"""A tuple with the width and height of the console in characters and lines."""
|
|
113
130
|
usr: str = _ConsoleUser() # type: ignore[assignment]
|
|
114
131
|
"""The name of the current user."""
|
|
115
132
|
|
|
116
133
|
@staticmethod
|
|
117
134
|
def get_args(
|
|
118
|
-
find_args:
|
|
135
|
+
find_args: Mapping[str, list[str] | tuple[str, ...] | dict[str, list[str] | tuple[str, ...] | Any]],
|
|
119
136
|
allow_spaces: bool = False
|
|
120
137
|
) -> Args:
|
|
121
138
|
"""Will search for the specified arguments in the command line
|
|
@@ -223,13 +240,13 @@ class Console:
|
|
|
223
240
|
exit_code: int = 0,
|
|
224
241
|
reset_ansi: bool = False,
|
|
225
242
|
) -> None:
|
|
226
|
-
"""Will print the `
|
|
243
|
+
"""Will print the `prompt` and then pause the program if `pause` is set
|
|
227
244
|
to `True` and after the pause, exit the program if `exit` is set to `True`."""
|
|
228
245
|
print(prompt, end="", flush=True)
|
|
229
246
|
if reset_ansi:
|
|
230
247
|
FormatCodes.print("[_]", end="")
|
|
231
248
|
if pause:
|
|
232
|
-
_keyboard.
|
|
249
|
+
_keyboard.read_key(suppress=True)
|
|
233
250
|
if exit:
|
|
234
251
|
_sys.exit(exit_code)
|
|
235
252
|
|
|
@@ -267,7 +284,11 @@ class Console:
|
|
|
267
284
|
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
268
285
|
title = "" if title is None else title.strip().upper()
|
|
269
286
|
title_len, tab_len = len(title) + 4, _console_tabsize - ((len(title) + 4) % _console_tabsize)
|
|
270
|
-
|
|
287
|
+
if title_bg_color is not None and Color.is_valid(title_bg_color):
|
|
288
|
+
title_bg_color = Color.to_hexa(title_bg_color)
|
|
289
|
+
title_color = Color.text_color_for_on_bg(title_bg_color)
|
|
290
|
+
else:
|
|
291
|
+
title_color = "_color" if title_bg_color is None else "#000"
|
|
271
292
|
if format_linebreaks:
|
|
272
293
|
clean_prompt, removals = FormatCodes.remove_formatting(str(prompt), get_removals=True, _ignore_linebreaks=True)
|
|
273
294
|
prompt_lst = (String.split_count(l, Console.w - (title_len + tab_len)) for l in str(clean_prompt).splitlines())
|
|
@@ -275,7 +296,7 @@ class Console:
|
|
|
275
296
|
item for lst in prompt_lst for item in ([""] if lst == [] else (lst if isinstance(lst, list) else [lst]))
|
|
276
297
|
)
|
|
277
298
|
prompt = f"\n{' ' * title_len}\t".join(
|
|
278
|
-
Console.__add_back_removed_parts(list(prompt_lst),
|
|
299
|
+
Console.__add_back_removed_parts(list(prompt_lst), cast(tuple[tuple[int, str], ...], removals))
|
|
279
300
|
)
|
|
280
301
|
else:
|
|
281
302
|
prompt = str(prompt)
|
|
@@ -332,8 +353,8 @@ class Console:
|
|
|
332
353
|
format_linebreaks: bool = True,
|
|
333
354
|
start: str = "",
|
|
334
355
|
end: str = "\n",
|
|
335
|
-
title_bg_color: Rgba | Hexa = COLOR.yellow,
|
|
336
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
356
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.yellow,
|
|
357
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
337
358
|
pause: bool = False,
|
|
338
359
|
exit: bool = False,
|
|
339
360
|
) -> None:
|
|
@@ -350,8 +371,8 @@ class Console:
|
|
|
350
371
|
format_linebreaks: bool = True,
|
|
351
372
|
start: str = "",
|
|
352
373
|
end: str = "\n",
|
|
353
|
-
title_bg_color: Rgba | Hexa = COLOR.blue,
|
|
354
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
374
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.blue,
|
|
375
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
355
376
|
pause: bool = False,
|
|
356
377
|
exit: bool = False,
|
|
357
378
|
) -> None:
|
|
@@ -366,8 +387,8 @@ class Console:
|
|
|
366
387
|
format_linebreaks: bool = True,
|
|
367
388
|
start: str = "",
|
|
368
389
|
end: str = "\n",
|
|
369
|
-
title_bg_color: Rgba | Hexa = COLOR.teal,
|
|
370
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
390
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.teal,
|
|
391
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
371
392
|
pause: bool = False,
|
|
372
393
|
exit: bool = False,
|
|
373
394
|
) -> None:
|
|
@@ -382,8 +403,8 @@ class Console:
|
|
|
382
403
|
format_linebreaks: bool = True,
|
|
383
404
|
start: str = "",
|
|
384
405
|
end: str = "\n",
|
|
385
|
-
title_bg_color: Rgba | Hexa = COLOR.orange,
|
|
386
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
406
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.orange,
|
|
407
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
387
408
|
pause: bool = False,
|
|
388
409
|
exit: bool = False,
|
|
389
410
|
) -> None:
|
|
@@ -398,11 +419,11 @@ class Console:
|
|
|
398
419
|
format_linebreaks: bool = True,
|
|
399
420
|
start: str = "",
|
|
400
421
|
end: str = "\n",
|
|
401
|
-
title_bg_color: Rgba | Hexa = COLOR.red,
|
|
402
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
422
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.red,
|
|
423
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
403
424
|
pause: bool = False,
|
|
404
425
|
exit: bool = True,
|
|
405
|
-
reset_ansi=True,
|
|
426
|
+
reset_ansi: bool = True,
|
|
406
427
|
) -> None:
|
|
407
428
|
"""A preset for `log()`: `FAIL` log message with the options to pause
|
|
408
429
|
at the message and exit the program after the message was printed."""
|
|
@@ -415,11 +436,11 @@ class Console:
|
|
|
415
436
|
format_linebreaks: bool = True,
|
|
416
437
|
start: str = "",
|
|
417
438
|
end: str = "\n",
|
|
418
|
-
title_bg_color: Rgba | Hexa = COLOR.magenta,
|
|
419
|
-
default_color: Rgba | Hexa = COLOR.text,
|
|
439
|
+
title_bg_color: Optional[Rgba | Hexa] = COLOR.magenta,
|
|
440
|
+
default_color: Optional[Rgba | Hexa] = COLOR.text,
|
|
420
441
|
pause: bool = False,
|
|
421
442
|
exit: bool = True,
|
|
422
|
-
reset_ansi=True,
|
|
443
|
+
reset_ansi: bool = True,
|
|
423
444
|
) -> None:
|
|
424
445
|
"""A preset for `log()`: `EXIT` log message with the options to pause
|
|
425
446
|
at the message and exit the program after the message was printed."""
|
|
@@ -427,18 +448,18 @@ class Console:
|
|
|
427
448
|
Console.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
428
449
|
|
|
429
450
|
@staticmethod
|
|
430
|
-
def
|
|
451
|
+
def log_box_filled(
|
|
431
452
|
*values: object,
|
|
432
453
|
start: str = "",
|
|
433
454
|
end: str = "\n",
|
|
434
455
|
box_bg_color: str | Rgba | Hexa = "green",
|
|
435
|
-
default_color: Rgba | Hexa =
|
|
456
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
436
457
|
w_padding: int = 2,
|
|
437
458
|
w_full: bool = False,
|
|
438
459
|
) -> None:
|
|
439
|
-
"""Will print a box, containing a formatted log message:
|
|
460
|
+
"""Will print a box with a colored background, containing a formatted log message:
|
|
440
461
|
- `*values` -⠀the box content (each value is on a new line)
|
|
441
|
-
- `start` -⠀something to print before the log box is printed
|
|
462
|
+
- `start` -⠀something to print before the log box is printed (e.g. `\\n`)
|
|
442
463
|
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
443
464
|
- `box_bg_color` -⠀the background color of the box
|
|
444
465
|
- `default_color` -⠀the default text color of the `*values`
|
|
@@ -447,30 +468,108 @@ class Console:
|
|
|
447
468
|
-----------------------------------------------------------------------------------
|
|
448
469
|
The box content can be formatted with special formatting codes. For more detailed
|
|
449
470
|
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
450
|
-
lines =
|
|
451
|
-
unfmt_lines = [FormatCodes.remove_formatting(line) for line in lines]
|
|
452
|
-
max_line_len = max(len(line) for line in unfmt_lines)
|
|
471
|
+
lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
|
|
453
472
|
pad_w_full = (Console.w - (max_line_len + (2 * w_padding))) if w_full else 0
|
|
473
|
+
if box_bg_color is not None and Color.is_valid(box_bg_color):
|
|
474
|
+
box_bg_color = Color.to_hexa(box_bg_color)
|
|
454
475
|
lines = [
|
|
455
476
|
f"[bg:{box_bg_color}]{' ' * w_padding}{line}" + " " *
|
|
456
|
-
((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[
|
|
477
|
+
((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[*]" for line, unfmt in zip(lines, unfmt_lines)
|
|
457
478
|
]
|
|
458
479
|
pady = " " * (Console.w if w_full else max_line_len + (2 * w_padding))
|
|
459
480
|
FormatCodes.print(
|
|
460
|
-
f"{start}[bg:{box_bg_color}]{pady}[
|
|
481
|
+
f"{start}[bg:{box_bg_color}]{pady}[*]\n"
|
|
461
482
|
+ _COMPILED["formatting"].sub(lambda m: f"{m.group(0)}[bg:{box_bg_color}]", "\n".join(lines))
|
|
462
|
-
+ f"\n[bg:{box_bg_color}]{pady}[
|
|
483
|
+
+ f"\n[bg:{box_bg_color}]{pady}[_]",
|
|
484
|
+
default_color=default_color or "#000",
|
|
485
|
+
sep="\n",
|
|
486
|
+
end=end,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
@staticmethod
|
|
490
|
+
def log_box_bordered(
|
|
491
|
+
*values: object,
|
|
492
|
+
start: str = "",
|
|
493
|
+
end: str = "\n",
|
|
494
|
+
border_type: Literal["standard", "rounded", "strong", "double"] = "rounded",
|
|
495
|
+
border_style: str | Rgba | Hexa = f"dim|{COLOR.gray}",
|
|
496
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
497
|
+
w_padding: int = 1,
|
|
498
|
+
w_full: bool = False,
|
|
499
|
+
_border_chars: Optional[tuple[str, str, str, str, str, str, str, str]] = None,
|
|
500
|
+
) -> None:
|
|
501
|
+
"""Will print a bordered box, containing a formatted log message:
|
|
502
|
+
- `*values` -⠀the box content (each value is on a new line)
|
|
503
|
+
- `start` -⠀something to print before the log box is printed (e.g. `\\n`)
|
|
504
|
+
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
505
|
+
- `border_type` -⠀one of the predefined border character sets
|
|
506
|
+
- `border_style` -⠀the style of the border (special formatting codes)
|
|
507
|
+
- `default_color` -⠀the default text color of the `*values`
|
|
508
|
+
- `w_padding` -⠀the horizontal padding (in chars) to the box content
|
|
509
|
+
- `w_full` -⠀whether to make the box be the full console width or not
|
|
510
|
+
- `_border_chars` -⠀define your own border characters set (overwrites `border_type`)\n
|
|
511
|
+
---------------------------------------------------------------------------------------
|
|
512
|
+
The box content can be formatted with special formatting codes. For more detailed
|
|
513
|
+
information about formatting codes, see `xx_format_codes` module documentation.\n
|
|
514
|
+
---------------------------------------------------------------------------------------
|
|
515
|
+
The `border_type` can be one of the following:
|
|
516
|
+
- `"standard" = ('┌', '─', '┐', '│', '┘', '─', '└', '│')`
|
|
517
|
+
- `"rounded" = ('╭', '─', '╮', '│', '╯', '─', '╰', '│')`
|
|
518
|
+
- `"strong" = ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃')`
|
|
519
|
+
- `"double" = ('╔', '═', '╗', '║', '╝', '═', '╚', '║')`\n
|
|
520
|
+
The order of the characters is always:
|
|
521
|
+
1. top-left corner
|
|
522
|
+
2. top border
|
|
523
|
+
3. top-right corner
|
|
524
|
+
4. right border
|
|
525
|
+
5. bottom-right corner
|
|
526
|
+
6. bottom border
|
|
527
|
+
7. bottom-left corner
|
|
528
|
+
8. left border"""
|
|
529
|
+
borders = {
|
|
530
|
+
"standard": ('┌', '─', '┐', '│', '┘', '─', '└', '│'),
|
|
531
|
+
"rounded": ('╭', '─', '╮', '│', '╯', '─', '╰', '│'),
|
|
532
|
+
"strong": ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃'),
|
|
533
|
+
"double": ('╔', '═', '╗', '║', '╝', '═', '╚', '║'),
|
|
534
|
+
}
|
|
535
|
+
border_chars = borders.get(border_type, borders["standard"]) if _border_chars is None else _border_chars
|
|
536
|
+
lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
|
|
537
|
+
print(unfmt_lines)
|
|
538
|
+
pad_w_full = (Console.w - (max_line_len + (2 * w_padding)) - (len(border_chars[1] * 2))) if w_full else 0
|
|
539
|
+
if border_style is not None and Color.is_valid(border_style):
|
|
540
|
+
border_style = Color.to_hexa(border_style)
|
|
541
|
+
border_l = f"[{border_style}]{border_chars[7]}[*]"
|
|
542
|
+
border_r = f"[{border_style}]{border_chars[3]}[_]"
|
|
543
|
+
lines = [
|
|
544
|
+
f"{border_l}{' ' * w_padding}{line}[_]" + " " * ((w_padding + max_line_len - len(unfmt)) + pad_w_full) + border_r
|
|
545
|
+
for line, unfmt in zip(lines, unfmt_lines)
|
|
546
|
+
]
|
|
547
|
+
border_t = f"[{border_style}]{border_chars[0]}{border_chars[1] * (Console.w - (len(border_chars[1] * 2)) if w_full else max_line_len + (2 * w_padding))}{border_chars[2]}[_]"
|
|
548
|
+
border_b = f"[{border_style}]{border_chars[6]}{border_chars[5] * (Console.w - (len(border_chars[1] * 2)) if w_full else max_line_len + (2 * w_padding))}{border_chars[4]}[_]"
|
|
549
|
+
FormatCodes.print(
|
|
550
|
+
f"{start}{border_t}[_]\n" + "\n".join(lines) + f"\n{border_b}[_]",
|
|
463
551
|
default_color=default_color,
|
|
464
552
|
sep="\n",
|
|
465
553
|
end=end,
|
|
466
554
|
)
|
|
467
555
|
|
|
556
|
+
@staticmethod
|
|
557
|
+
def __prepare_log_box(
|
|
558
|
+
values: tuple[object, ...],
|
|
559
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
560
|
+
) -> tuple[list[str], list[tuple[str, tuple[tuple[int, str], ...]]], int]:
|
|
561
|
+
"""Prepares the log box content and returns it along with the max line length."""
|
|
562
|
+
lines = [line for val in values for line in str(val).splitlines()]
|
|
563
|
+
unfmt_lines = [FormatCodes.remove_formatting(line, default_color) for line in lines]
|
|
564
|
+
max_line_len = max(len(line) for line in unfmt_lines)
|
|
565
|
+
return lines, cast(list[tuple[str, tuple[tuple[int, str], ...]]], unfmt_lines), max_line_len
|
|
566
|
+
|
|
468
567
|
@staticmethod
|
|
469
568
|
def confirm(
|
|
470
569
|
prompt: object = "Do you want to continue?",
|
|
471
570
|
start="",
|
|
472
571
|
end="\n",
|
|
473
|
-
default_color: Rgba | Hexa = COLOR.cyan,
|
|
572
|
+
default_color: Optional[Rgba | Hexa] = COLOR.cyan,
|
|
474
573
|
default_is_yes: bool = True,
|
|
475
574
|
) -> bool:
|
|
476
575
|
"""Ask a yes/no question.\n
|
|
@@ -480,7 +579,7 @@ class Console:
|
|
|
480
579
|
confirmed = input(
|
|
481
580
|
FormatCodes.to_ansi(
|
|
482
581
|
f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )',
|
|
483
|
-
default_color,
|
|
582
|
+
default_color=default_color,
|
|
484
583
|
)
|
|
485
584
|
).strip().lower() in (("", "y", "yes") if default_is_yes else ("y", "yes"))
|
|
486
585
|
if end:
|
|
@@ -492,9 +591,9 @@ class Console:
|
|
|
492
591
|
prompt: object = "",
|
|
493
592
|
start="",
|
|
494
593
|
end="\n",
|
|
495
|
-
default_color: Rgba | Hexa = COLOR.cyan,
|
|
594
|
+
default_color: Optional[Rgba | Hexa] = COLOR.cyan,
|
|
496
595
|
show_keybindings=True,
|
|
497
|
-
input_prefix="
|
|
596
|
+
input_prefix=" ⮡ ",
|
|
498
597
|
reset_ansi=True,
|
|
499
598
|
) -> str:
|
|
500
599
|
"""An input where users can input (and paste) text over multiple lines.\n
|
|
@@ -527,7 +626,7 @@ class Console:
|
|
|
527
626
|
prompt: object = "",
|
|
528
627
|
start="",
|
|
529
628
|
end="\n",
|
|
530
|
-
default_color: Rgba | Hexa = COLOR.cyan,
|
|
629
|
+
default_color: Optional[Rgba | Hexa] = COLOR.cyan,
|
|
531
630
|
allowed_chars: str = CHARS.all, # type: ignore[assignment]
|
|
532
631
|
min_len: Optional[int] = None,
|
|
533
632
|
max_len: Optional[int] = None,
|
|
@@ -631,7 +730,7 @@ class Console:
|
|
|
631
730
|
prompt: object = "Password: ",
|
|
632
731
|
start="",
|
|
633
732
|
end="\n",
|
|
634
|
-
default_color: Rgba | Hexa = COLOR.cyan,
|
|
733
|
+
default_color: Optional[Rgba | Hexa] = COLOR.cyan,
|
|
635
734
|
allowed_chars: str = CHARS.standard_ascii,
|
|
636
735
|
min_len: Optional[int] = None,
|
|
637
736
|
max_len: Optional[int] = None,
|
xulbux/xx_format_codes.py
CHANGED
|
@@ -133,9 +133,13 @@ the formatting code:
|
|
|
133
133
|
#### Additional Formatting Codes when a `default_color` is set
|
|
134
134
|
|
|
135
135
|
1. `[*]` resets everything, just like `[_]`, but the text color will remain in `default_color`
|
|
136
|
-
|
|
136
|
+
(if no `default_color` is set, it resets everything, exactly like `[_]`)
|
|
137
|
+
2. `[*color]` `[*c]` will reset the text color, just like `[_color]`, but then also make it `default_color`
|
|
138
|
+
(if no `default_color` is set, both are treated as invalid formatting codes)
|
|
137
139
|
3. `[default]` will just color the text in `default_color`
|
|
140
|
+
(if no `default_color` is set, it's treated as an invalid formatting code)
|
|
138
141
|
4. `[background:default]` `[BG:default]` will color the background in `default_color`
|
|
142
|
+
(if no `default_color` is set, both are treated as invalid formatting codes)\n
|
|
139
143
|
|
|
140
144
|
Unlike the standard console colors, the default color can be changed by using the following modifiers:
|
|
141
145
|
|
|
@@ -148,6 +152,7 @@ Unlike the standard console colors, the default color can be changed by using th
|
|
|
148
152
|
- `[ddd]` will darken the `default_color` text by `3 × brightness_steps`%
|
|
149
153
|
- ... etc.
|
|
150
154
|
Per default, you can also use `+` and `-` to get lighter and darker `default_color` versions.
|
|
155
|
+
All of these lighten/darken formatting codes are treated as invalid if no `default_color` is set.
|
|
151
156
|
"""
|
|
152
157
|
|
|
153
158
|
from ._consts_ import ANSI
|
|
@@ -155,7 +160,7 @@ from .xx_string import String
|
|
|
155
160
|
from .xx_regex import Regex, Match, Pattern
|
|
156
161
|
from .xx_color import Color, rgba, Rgba, Hexa
|
|
157
162
|
|
|
158
|
-
from typing import Optional
|
|
163
|
+
from typing import Optional, cast
|
|
159
164
|
import ctypes as _ctypes
|
|
160
165
|
import regex as _rx
|
|
161
166
|
import sys as _sys
|
|
@@ -164,6 +169,11 @@ import re as _re
|
|
|
164
169
|
|
|
165
170
|
_CONSOLE_ANSI_CONFIGURED: bool = False
|
|
166
171
|
|
|
172
|
+
_ANSI_SEQ_1: str = ANSI.seq(1)
|
|
173
|
+
_DEFAULT_COLOR_MODS: dict[str, str] = {
|
|
174
|
+
"lighten": "+l",
|
|
175
|
+
"darken": "-d",
|
|
176
|
+
}
|
|
167
177
|
_PREFIX: dict[str, set[str]] = {
|
|
168
178
|
"BG": {"background", "bg"},
|
|
169
179
|
"BR": {"bright", "br"},
|
|
@@ -174,7 +184,7 @@ _PREFIX_RX: dict[str, str] = {
|
|
|
174
184
|
}
|
|
175
185
|
_COMPILED: dict[str, Pattern] = { # PRECOMPILE REGULAR EXPRESSIONS
|
|
176
186
|
"*": _re.compile(r"\[\s*([^]_]*?)\s*\*\s*([^]_]*?)\]"),
|
|
177
|
-
"*color": _re.compile(r"\[\s*([^]_]*?)\s*\*
|
|
187
|
+
"*color": _re.compile(r"\[\s*([^]_]*?)\s*\*c(?:olor)?\s*([^]_]*?)\]"),
|
|
178
188
|
"ansi_seq": _re.compile(ANSI.char + r"(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"),
|
|
179
189
|
"formatting": _rx.compile(
|
|
180
190
|
Regex.brackets("[", "]", is_group=True, ignore_in_strings=False)
|
|
@@ -189,7 +199,7 @@ _COMPILED: dict[str, Pattern] = { # PRECOMPILE REGULAR EXPRESSIONS
|
|
|
189
199
|
"modifier": _re.compile(
|
|
190
200
|
r"(?i)((?:BG\s*:)?)\s*("
|
|
191
201
|
+ "|".join(
|
|
192
|
-
[f"{_re.escape(m)}+" for m in
|
|
202
|
+
[f"{_re.escape(m)}+" for m in _DEFAULT_COLOR_MODS["lighten"] + _DEFAULT_COLOR_MODS["darken"]]
|
|
193
203
|
)
|
|
194
204
|
+ r")$"
|
|
195
205
|
),
|
|
@@ -246,6 +256,7 @@ class FormatCodes:
|
|
|
246
256
|
default_color: Optional[Rgba | Hexa] = None,
|
|
247
257
|
brightness_steps: int = 20,
|
|
248
258
|
_default_start: bool = True,
|
|
259
|
+
_validate_default: bool = True,
|
|
249
260
|
) -> str:
|
|
250
261
|
"""Convert the formatting codes inside a string to ANSI formatting.\n
|
|
251
262
|
-------------------------------------------------------------------------
|
|
@@ -253,15 +264,22 @@ class FormatCodes:
|
|
|
253
264
|
`xx_format_codes` module documentation."""
|
|
254
265
|
if not isinstance(string, str):
|
|
255
266
|
string = str(string)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
267
|
+
use_default, default_specified = False, default_color is not None
|
|
268
|
+
if _validate_default and default_specified:
|
|
269
|
+
if Color.is_valid_rgba(default_color, False):
|
|
270
|
+
use_default = True
|
|
271
|
+
elif Color.is_valid_hexa(default_color, False):
|
|
272
|
+
use_default, default_color = True, Color.to_rgba(default_color) # type: ignore[assignment]
|
|
260
273
|
else:
|
|
261
|
-
use_default =
|
|
274
|
+
use_default = default_specified
|
|
275
|
+
default_color = cast(Optional[rgba], default_color)
|
|
262
276
|
if use_default:
|
|
263
277
|
string = _COMPILED["*"].sub(r"[\1_|default\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|default|…]`
|
|
264
|
-
string = _COMPILED["*color"].sub(
|
|
278
|
+
string = _COMPILED["*color"].sub(
|
|
279
|
+
r"[\1default\2]", string
|
|
280
|
+
) # REPLACE `[…|*color|…]` OR `[…|*c|…]` WITH `[…|default|…]`
|
|
281
|
+
else:
|
|
282
|
+
string = _COMPILED["*"].sub(r"[\1_\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|…]`
|
|
265
283
|
|
|
266
284
|
def is_valid_color(color: str) -> bool:
|
|
267
285
|
return bool((color in ANSI.color_map) or Color.is_valid_rgba(color) or Color.is_valid_hexa(color))
|
|
@@ -273,15 +291,27 @@ class FormatCodes:
|
|
|
273
291
|
if formats_escaped := bool(_COMPILED["escape_char_cond"].match(match.group(0))):
|
|
274
292
|
_formats = formats = _COMPILED["escape_char"].sub(r"\1", formats) # REMOVE / OR \\
|
|
275
293
|
if auto_reset_txt and auto_reset_txt.count("[") > 0 and auto_reset_txt.count("]") > 0:
|
|
276
|
-
auto_reset_txt = FormatCodes.to_ansi(
|
|
294
|
+
auto_reset_txt = FormatCodes.to_ansi(
|
|
295
|
+
auto_reset_txt,
|
|
296
|
+
default_color,
|
|
297
|
+
brightness_steps,
|
|
298
|
+
_default_start=False,
|
|
299
|
+
_validate_default=False,
|
|
300
|
+
)
|
|
277
301
|
if not formats:
|
|
278
302
|
return match.group(0)
|
|
279
303
|
if formats.count("[") > 0 and formats.count("]") > 0:
|
|
280
|
-
formats = FormatCodes.to_ansi(
|
|
304
|
+
formats = FormatCodes.to_ansi(
|
|
305
|
+
formats,
|
|
306
|
+
default_color,
|
|
307
|
+
brightness_steps,
|
|
308
|
+
_default_start=False,
|
|
309
|
+
_validate_default=False,
|
|
310
|
+
)
|
|
281
311
|
format_keys = [k.strip() for k in formats.split("|") if k.strip()]
|
|
282
312
|
ansi_formats = [
|
|
283
|
-
r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k
|
|
284
|
-
|
|
313
|
+
r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k else f"[{k}]"
|
|
314
|
+
for k in format_keys
|
|
285
315
|
]
|
|
286
316
|
if auto_reset_txt and not auto_reset_escaped:
|
|
287
317
|
reset_keys = []
|
|
@@ -292,7 +322,7 @@ class FormatCodes:
|
|
|
292
322
|
if k_set & _PREFIX["BR"]:
|
|
293
323
|
for i in range(len(k)):
|
|
294
324
|
if is_valid_color(k[i:]):
|
|
295
|
-
reset_keys.extend(["_bg", "
|
|
325
|
+
reset_keys.extend(["_bg", "default"] if use_default else ["_bg", "_c"])
|
|
296
326
|
break
|
|
297
327
|
else:
|
|
298
328
|
for i in range(len(k)):
|
|
@@ -302,13 +332,12 @@ class FormatCodes:
|
|
|
302
332
|
elif is_valid_color(k) or any(
|
|
303
333
|
k_lower.startswith(pref_colon := f"{prefix}:") and is_valid_color(k[len(pref_colon):])
|
|
304
334
|
for prefix in _PREFIX["BR"]):
|
|
305
|
-
reset_keys.append("
|
|
335
|
+
reset_keys.append("default" if use_default else "_c")
|
|
306
336
|
else:
|
|
307
337
|
reset_keys.append(f"_{k}")
|
|
308
338
|
ansi_resets = [
|
|
309
|
-
r for k in reset_keys
|
|
310
|
-
|
|
311
|
-
).startswith(f"{ANSI.char}{ANSI.start}")
|
|
339
|
+
r for k in reset_keys if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)
|
|
340
|
+
).startswith(f"{ANSI.char}{ANSI.start}")
|
|
312
341
|
]
|
|
313
342
|
else:
|
|
314
343
|
ansi_resets = []
|
|
@@ -320,14 +349,14 @@ class FormatCodes:
|
|
|
320
349
|
else:
|
|
321
350
|
return (
|
|
322
351
|
"".join(ansi_formats) + (
|
|
323
|
-
f"({FormatCodes.to_ansi(auto_reset_txt, default_color, brightness_steps, False)})"
|
|
352
|
+
f"({FormatCodes.to_ansi(auto_reset_txt, default_color, brightness_steps, _default_start=False, _validate_default=False)})"
|
|
324
353
|
if auto_reset_escaped and auto_reset_txt else auto_reset_txt if auto_reset_txt else ""
|
|
325
354
|
) + ("" if auto_reset_escaped else "".join(ansi_resets))
|
|
326
355
|
)
|
|
327
356
|
|
|
328
357
|
string = "\n".join(_COMPILED["formatting"].sub(replace_keys, line) for line in string.split("\n"))
|
|
329
|
-
return (((FormatCodes.__get_default_ansi(default_color) or "") if _default_start else "")
|
|
330
|
-
+ string) if
|
|
358
|
+
return (((FormatCodes.__get_default_ansi(default_color) or "") if _default_start else "")
|
|
359
|
+
+ string) if default_color is not None else string
|
|
331
360
|
|
|
332
361
|
@staticmethod
|
|
333
362
|
def escape_ansi(ansi_string: str) -> str:
|
|
@@ -366,6 +395,7 @@ class FormatCodes:
|
|
|
366
395
|
@staticmethod
|
|
367
396
|
def remove_formatting(
|
|
368
397
|
string: str,
|
|
398
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
369
399
|
get_removals: bool = False,
|
|
370
400
|
_ignore_linebreaks: bool = False,
|
|
371
401
|
) -> str | tuple[str, tuple[tuple[int, str], ...]]:
|
|
@@ -375,7 +405,7 @@ class FormatCodes:
|
|
|
375
405
|
Each tuple contains the position of the removed formatting code and the removed formatting code.\n
|
|
376
406
|
If `_ignore_linebreaks` is true, linebreaks will be ignored for the removal positions."""
|
|
377
407
|
return FormatCodes.remove_ansi(
|
|
378
|
-
FormatCodes.to_ansi(string),
|
|
408
|
+
FormatCodes.to_ansi(string, default_color=default_color),
|
|
379
409
|
get_removals=get_removals,
|
|
380
410
|
_ignore_linebreaks=_ignore_linebreaks,
|
|
381
411
|
)
|
|
@@ -395,19 +425,21 @@ class FormatCodes:
|
|
|
395
425
|
|
|
396
426
|
@staticmethod
|
|
397
427
|
def __get_default_ansi(
|
|
398
|
-
default_color:
|
|
428
|
+
default_color: rgba,
|
|
399
429
|
format_key: Optional[str] = None,
|
|
400
430
|
brightness_steps: Optional[int] = None,
|
|
401
|
-
_modifiers: tuple[str, str] = (
|
|
431
|
+
_modifiers: tuple[str, str] = (_DEFAULT_COLOR_MODS["lighten"], _DEFAULT_COLOR_MODS["darken"]),
|
|
402
432
|
) -> Optional[str]:
|
|
403
433
|
"""Get the `default_color` and lighter/darker versions of it as ANSI code."""
|
|
404
|
-
if not
|
|
434
|
+
if not isinstance(default_color, rgba):
|
|
435
|
+
return None
|
|
436
|
+
_default_color: tuple[int, int, int] = tuple(default_color)[:3]
|
|
437
|
+
if brightness_steps is None or (format_key and _COMPILED["bg?_default"].search(format_key)):
|
|
405
438
|
return (ANSI.seq_bg_color if format_key and _COMPILED["bg_default"].search(format_key) else ANSI.seq_color).format(
|
|
406
|
-
*
|
|
439
|
+
*_default_color
|
|
407
440
|
)
|
|
408
441
|
if format_key is None or not (format_key in _modifiers[0] or format_key in _modifiers[1]):
|
|
409
442
|
return None
|
|
410
|
-
assert format_key is not None
|
|
411
443
|
match = _COMPILED["modifier"].match(format_key)
|
|
412
444
|
if not match:
|
|
413
445
|
return None
|
|
@@ -418,7 +450,7 @@ class FormatCodes:
|
|
|
418
450
|
if adjust and adjust > 0:
|
|
419
451
|
modifiers = mod
|
|
420
452
|
break
|
|
421
|
-
new_rgb =
|
|
453
|
+
new_rgb = _default_color
|
|
422
454
|
if adjust == 0:
|
|
423
455
|
return None
|
|
424
456
|
elif modifiers in _modifiers[0]:
|
|
@@ -428,19 +460,17 @@ class FormatCodes:
|
|
|
428
460
|
return (ANSI.seq_bg_color if is_bg else ANSI.seq_color).format(*new_rgb[:3])
|
|
429
461
|
|
|
430
462
|
@staticmethod
|
|
431
|
-
def __get_replacement(format_key: str, default_color: Optional[
|
|
463
|
+
def __get_replacement(format_key: str, default_color: Optional[rgba], brightness_steps: int = 20) -> str:
|
|
432
464
|
"""Gives you the corresponding ANSI code for the given format key.
|
|
433
465
|
If `default_color` is not `None`, the text color will be `default_color` if all formats
|
|
434
466
|
are reset or you can get lighter or darker version of `default_color` (also as BG)"""
|
|
435
|
-
use_default = default_color and Color.is_valid_rgba(default_color, False)
|
|
436
|
-
_default_color = tuple(Color.to_rgba(default_color)) if use_default else () # type: ignore[assignment]
|
|
437
467
|
_format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
|
|
438
|
-
if
|
|
439
|
-
|
|
440
|
-
|
|
468
|
+
if default_color and (new_default_color := FormatCodes.__get_default_ansi(default_color, format_key,
|
|
469
|
+
brightness_steps)):
|
|
470
|
+
return new_default_color
|
|
441
471
|
for map_key in ANSI.codes_map:
|
|
442
472
|
if (isinstance(map_key, tuple) and format_key in map_key) or format_key == map_key:
|
|
443
|
-
return
|
|
473
|
+
return _ANSI_SEQ_1.format(
|
|
444
474
|
next((
|
|
445
475
|
v for k, v in ANSI.codes_map.items() if format_key == k or (isinstance(k, tuple) and format_key in k)
|
|
446
476
|
), None)
|
xulbux/xx_regex.py
CHANGED
|
@@ -11,13 +11,13 @@ class Regex:
|
|
|
11
11
|
|
|
12
12
|
@staticmethod
|
|
13
13
|
def quotes() -> str:
|
|
14
|
-
"""Matches
|
|
14
|
+
"""Matches pairs of quotes. (strings)\n
|
|
15
15
|
--------------------------------------------------------------------------------
|
|
16
16
|
Will create two named groups:
|
|
17
17
|
- `quote` the quote type (single or double)
|
|
18
18
|
- `string` everything inside the found quote pair\n
|
|
19
|
-
|
|
20
|
-
Attention: Requires non
|
|
19
|
+
---------------------------------------------------------------------------------
|
|
20
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
21
21
|
return r'(?P<quote>[\'"])(?P<string>(?:\\.|(?!\g<quote>).)*?)\g<quote>'
|
|
22
22
|
|
|
23
23
|
@staticmethod
|
|
@@ -28,16 +28,16 @@ class Regex:
|
|
|
28
28
|
strip_spaces: bool = True,
|
|
29
29
|
ignore_in_strings: bool = True,
|
|
30
30
|
) -> str:
|
|
31
|
-
"""Matches everything inside brackets, including other nested brackets.\n
|
|
32
|
-
|
|
31
|
+
"""Matches everything inside pairs of brackets, including other nested brackets.\n
|
|
32
|
+
-----------------------------------------------------------------------------------
|
|
33
33
|
If `is_group` is true, you will be able to reference the matched content as a
|
|
34
34
|
group (e.g. `match.group(…)` or `r'\\…'`).
|
|
35
35
|
If `strip_spaces` is true, it will ignore spaces around the content inside the
|
|
36
36
|
brackets.
|
|
37
37
|
If `ignore_in_strings` is true and a bracket is inside a string (e.g. `'...'`
|
|
38
38
|
or `"..."`), it will not be counted as the matching closing bracket.\n
|
|
39
|
-
|
|
40
|
-
Attention: Requires non
|
|
39
|
+
-----------------------------------------------------------------------------------
|
|
40
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
41
41
|
g, b1, b2, s1, s2 = (
|
|
42
42
|
"" if is_group else "?:",
|
|
43
43
|
_rx.escape(bracket1) if len(bracket1) == 1 else bracket1,
|
|
@@ -73,8 +73,8 @@ class Regex:
|
|
|
73
73
|
1. function name
|
|
74
74
|
2. the function's arguments\n
|
|
75
75
|
If no `func_name` is given, it will match any function call.\n
|
|
76
|
-
|
|
77
|
-
Attention: Requires non
|
|
76
|
+
---------------------------------------------------------------------------------
|
|
77
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
78
78
|
return (
|
|
79
79
|
r"(?<=\b)(" + (r"[\w_]+" if func_name is None else func_name) + r")\s*" + Regex.brackets("(", ")", is_group=True)
|
|
80
80
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xulbux
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.2
|
|
4
4
|
Summary: A Python library which includes lots of helpful classes, types and functions aiming to make common programming tasks simpler.
|
|
5
5
|
Author-email: XulbuX <xulbux.real@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -34,14 +34,14 @@ Requires-Dist: flake8>=6.1.0; extra == "dev"
|
|
|
34
34
|
Requires-Dist: flake8-pyproject>=1.2.3; extra == "dev"
|
|
35
35
|
Dynamic: license-file
|
|
36
36
|
|
|
37
|
-
# **$\
|
|
37
|
+
# **$\Huge\textsf{XulbuX}$**
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
**XulbuX** is library that contains many useful classes, types, and functions,
|
|
40
40
|
ranging from console logging and working with colors to file management and system operations.
|
|
41
41
|
The library is designed to simplify common programming tasks and improve code readability through its collection of tools.
|
|
42
42
|
|
|
43
|
-
For precise information about the library, see the library's [wiki page](https://github.com/XulbuX/PythonLibraryXulbuX/wiki).<br>
|
|
44
|
-
For the libraries latest changes and updates, see the [change log](https://github.com/XulbuX/PythonLibraryXulbuX/blob/main/CHANGELOG.md).
|
|
43
|
+
For precise information about the library, see the library's [**wiki page**](https://github.com/XulbuX/PythonLibraryXulbuX/wiki).<br>
|
|
44
|
+
For the libraries latest changes and updates, see the [**change log**](https://github.com/XulbuX/PythonLibraryXulbuX/blob/main/CHANGELOG.md).
|
|
45
45
|
|
|
46
46
|
<br>
|
|
47
47
|
|
|
@@ -81,20 +81,20 @@ from xulbux import rgba, hsla, hexa
|
|
|
81
81
|
|
|
82
82
|
## Modules
|
|
83
83
|
|
|
84
|
-
| Module
|
|
85
|
-
|
|
|
86
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_code)
|
|
87
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_color)
|
|
88
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_console)
|
|
89
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_data)
|
|
84
|
+
| Module | Short Description |
|
|
85
|
+
| :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_code) | advanced code-string operations (*changing the indent, finding function calls, ...*) |
|
|
87
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_color) | everything around colors (*converting, blending, searching colors in strings, ...*) |
|
|
88
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_console) | advanced actions related to the console (*pretty logging, advanced inputs, ...*) |
|
|
89
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_data) | advanced operations with data structures (*compare, generate path ID's, pretty print/format, ...*) |
|
|
90
90
|
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_env_path) | getting and editing the PATH variable (*get paths, check for paths, add paths, ...*) |
|
|
91
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_file)
|
|
91
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_file) | advanced working with files (*create files, rename file-extensions, ...*) |
|
|
92
92
|
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_format_codes) | easy pretty printing with custom format codes (*print, inputs, custom format codes to ANSI, ...*) |
|
|
93
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_json)
|
|
94
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_path)
|
|
95
|
-
| 
|
|
96
|
-
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_string)
|
|
97
|
-
| 
|
|
93
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_json) | advanced working with json files (*read, create, update, ...*) |
|
|
94
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_path) | advanced path operations (*get paths, smart-extend relative paths, delete paths, ...*) |
|
|
95
|
+
|  | generated regex pattern-templates (*match bracket- and quote pairs, match colors, ...*) |
|
|
96
|
+
| [](https://github.com/XulbuX/PythonLibraryXulbuX/wiki/xx_string) | helpful actions when working with strings. (*normalize, escape, decompose, ...*) |
|
|
97
|
+
|  | advanced system actions (*restart with message, check installed Python libs, ...*) |
|
|
98
98
|
|
|
99
99
|
<br>
|
|
100
100
|
|
|
@@ -111,15 +111,15 @@ def main() -> None:
|
|
|
111
111
|
|
|
112
112
|
# LET THE USER ENTER A HEXA COLOR IN ANY HEXA FORMAT
|
|
113
113
|
input_clr = FormatCodes.input(
|
|
114
|
-
|
|
114
|
+
"\n[b](Enter a HEXA color in any format) [dim](>) "
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
# ANNOUNCE INDEXING THE INPUT COLOR
|
|
118
118
|
Console.log(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
"INDEX",
|
|
120
|
+
"Indexing the input HEXA color...",
|
|
121
|
+
start="\n",
|
|
122
|
+
title_bg_color=COLOR.blue,
|
|
123
123
|
)
|
|
124
124
|
|
|
125
125
|
try:
|
|
@@ -129,16 +129,16 @@ def main() -> None:
|
|
|
129
129
|
except ValueError:
|
|
130
130
|
# ANNOUNCE THE ERROR AND EXIT THE PROGRAM
|
|
131
131
|
Console.fail(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
"The input HEXA color is invalid.",
|
|
133
|
+
end="\n\n",
|
|
134
|
+
exit=True,
|
|
135
135
|
)
|
|
136
136
|
|
|
137
137
|
# ANNOUNCE STARTING THE CONVERSION
|
|
138
138
|
Console.log(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
"CONVERT",
|
|
140
|
+
"Converting the HEXA color into different types...",
|
|
141
|
+
title_bg_color=COLOR.tangerine,
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
# CONVERT THE HEXA COLOR INTO THE TWO OTHER COLOR TYPES
|
|
@@ -147,14 +147,16 @@ def main() -> None:
|
|
|
147
147
|
|
|
148
148
|
# ANNOUNCE THE SUCCESSFUL CONVERSION
|
|
149
149
|
Console.done(
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
"Successfully converted color into different types.",
|
|
151
|
+
end="\n\n",
|
|
152
152
|
)
|
|
153
153
|
|
|
154
154
|
# PRETTY PRINT THE COLOR IN DIFFERENT TYPES
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
Console.log_box_bordered(
|
|
156
|
+
f"[b](HEXA:) [i|white]({hexa_color})",
|
|
157
|
+
f"[b](RGBA:) [i|white]({rgba_color})",
|
|
158
|
+
f"[b](HSLA:) [i|white]({hsla_color})",
|
|
159
|
+
)
|
|
158
160
|
|
|
159
161
|
|
|
160
162
|
if __name__ == "__main__":
|
|
@@ -165,4 +167,4 @@ if __name__ == "__main__":
|
|
|
165
167
|
<br>
|
|
166
168
|
|
|
167
169
|
--------------------------------------------------------------
|
|
168
|
-
[View this library on PyPI](https://pypi.org/project/XulbuX/)
|
|
170
|
+
[View this library on **PyPI**](https://pypi.org/project/XulbuX/)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
xulbux/__init__.py,sha256=vd8y5L0AwqnxqXm_IAJ5grbfm8ZkcSHJx36n1KUQTlc,815
|
|
2
|
+
xulbux/_cli_.py,sha256=J4vfJHLJEYxCZzA_VJUB46w2WGShfdYFoetsLG5PfKo,3428
|
|
3
|
+
xulbux/_consts_.py,sha256=4CLJE-YH3si2YQ-IHcjsT5PW0D7vGvm8ZH13rUoG5cE,6166
|
|
4
|
+
xulbux/xx_code.py,sha256=b8MJId-BmZOsBH38z-h055op7qIICdsTT3rFJ7SWkiE,6111
|
|
5
|
+
xulbux/xx_color.py,sha256=Thj7fFTc8x-VmYyULI3HQW0uCk9dIihwrGUwuXGn83s,49744
|
|
6
|
+
xulbux/xx_console.py,sha256=c61EXVGQo_OL_MUWb4oXbIr6EKesm1v-FwxBIe47ZTg,34866
|
|
7
|
+
xulbux/xx_data.py,sha256=nEfVwK6-ILaL3K-bLezKpG1G7117CY5ZgC3BGwANrUE,30886
|
|
8
|
+
xulbux/xx_env_path.py,sha256=x56mKK4lSvU5yMCAs8k0RVIqXWUJcpcHYz5HoZ_RklM,4160
|
|
9
|
+
xulbux/xx_file.py,sha256=KerXOvKS93zIoAt36YTYuZboSmxVFVf2WcOrDcdwXfE,2627
|
|
10
|
+
xulbux/xx_format_codes.py,sha256=-tSsgy1tRelmU5x5ZS7ujaiXntyRqk1sm759tyvVIyU,24698
|
|
11
|
+
xulbux/xx_json.py,sha256=V7vdfpvSe9wpktR_c8zG_Meix7x9IRmn66k5nB3HUyo,7457
|
|
12
|
+
xulbux/xx_path.py,sha256=lLAEVZrW0TAwCewlONFVQcQ_8tVn9LTJZVOZpeGvE5s,7673
|
|
13
|
+
xulbux/xx_regex.py,sha256=_BtMHRDNcD9zF4SL87dQuUVZcYGfZx9H5YNSDiEtzm8,8059
|
|
14
|
+
xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
|
|
15
|
+
xulbux/xx_system.py,sha256=Tsx4wgztUg46KloqcGeiFkarDoM3EgJLXw3XNxgHBmU,6460
|
|
16
|
+
xulbux-1.7.2.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
|
|
17
|
+
xulbux-1.7.2.dist-info/METADATA,sha256=vlIkVkj97ZLjiFajkGrSZ5O0GDrzuIunJ1E1iqY-QTA,9209
|
|
18
|
+
xulbux-1.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
xulbux-1.7.2.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
|
|
20
|
+
xulbux-1.7.2.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
|
|
21
|
+
xulbux-1.7.2.dist-info/RECORD,,
|
xulbux-1.7.0.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
xulbux/__init__.py,sha256=TbXPioXPENwpR9t4lvqBBhV4cby0JGdIrSzzdMFYrEc,815
|
|
2
|
-
xulbux/_cli_.py,sha256=J4vfJHLJEYxCZzA_VJUB46w2WGShfdYFoetsLG5PfKo,3428
|
|
3
|
-
xulbux/_consts_.py,sha256=AuYTTmqrP2lawyVGlPLUaP1syxOoPA-ejJGH7WlwFzk,6314
|
|
4
|
-
xulbux/xx_code.py,sha256=w9yO-GPMeaE-xDi-L3VtpPpWpu5jOwagfMsG93aXANE,6106
|
|
5
|
-
xulbux/xx_color.py,sha256=ZEV9AG3MtgNMh4t4VexmBiaperan1pHO4d_VPCdv188,49923
|
|
6
|
-
xulbux/xx_console.py,sha256=WmH6YURaA-Y-LNB-2kZc8SuPxoZim8ZmzsuI01V9vcM,28682
|
|
7
|
-
xulbux/xx_data.py,sha256=nEfVwK6-ILaL3K-bLezKpG1G7117CY5ZgC3BGwANrUE,30886
|
|
8
|
-
xulbux/xx_env_path.py,sha256=x56mKK4lSvU5yMCAs8k0RVIqXWUJcpcHYz5HoZ_RklM,4160
|
|
9
|
-
xulbux/xx_file.py,sha256=KerXOvKS93zIoAt36YTYuZboSmxVFVf2WcOrDcdwXfE,2627
|
|
10
|
-
xulbux/xx_format_codes.py,sha256=fOFidFDBYV-rUXOlYTeKIZarfh9Q1jSUCGYyk8pg-ic,23397
|
|
11
|
-
xulbux/xx_json.py,sha256=V7vdfpvSe9wpktR_c8zG_Meix7x9IRmn66k5nB3HUyo,7457
|
|
12
|
-
xulbux/xx_path.py,sha256=lLAEVZrW0TAwCewlONFVQcQ_8tVn9LTJZVOZpeGvE5s,7673
|
|
13
|
-
xulbux/xx_regex.py,sha256=ejqVs4a-eCSTrSQfENoyl0AVztb8BuI2TNl-wzMQwYw,8048
|
|
14
|
-
xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
|
|
15
|
-
xulbux/xx_system.py,sha256=Tsx4wgztUg46KloqcGeiFkarDoM3EgJLXw3XNxgHBmU,6460
|
|
16
|
-
xulbux-1.7.0.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
|
|
17
|
-
xulbux-1.7.0.dist-info/METADATA,sha256=symGYQzZhSYyGcwM_dGI3FETAcqgTL-ft90CLtJoNlY,9222
|
|
18
|
-
xulbux-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
-
xulbux-1.7.0.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
|
|
20
|
-
xulbux-1.7.0.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
|
|
21
|
-
xulbux-1.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|