xulbux 1.7.0__py3-none-any.whl → 1.7.1__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/xx_color.py +45 -33
- xulbux/xx_console.py +116 -27
- xulbux/xx_format_codes.py +23 -19
- xulbux/xx_regex.py +9 -9
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/METADATA +36 -34
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/RECORD +11 -11
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/WHEEL +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/entry_points.txt +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/licenses/LICENSE +0 -0
- {xulbux-1.7.0.dist-info → xulbux-1.7.1.dist-info}/top_level.txt +0 -0
xulbux/__init__.py
CHANGED
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
|
|
@@ -48,25 +48,31 @@ class _ConsoleUser:
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class ArgResult:
|
|
51
|
-
"""
|
|
52
|
-
|
|
51
|
+
"""Represents the result of a parsed command-line argument and contains the following attributes:
|
|
52
|
+
- `exists` -⠀if the argument was found or not
|
|
53
|
+
- `value` -⠀the value given with the found argument\n
|
|
54
|
+
--------------------------------------------------------------------------------------------------------
|
|
55
|
+
When the `ArgResult` instance is accessed as a boolean it will correspond to the `exists` attribute."""
|
|
53
56
|
|
|
54
57
|
def __init__(self, exists: bool, value: Any):
|
|
55
|
-
self.exists = exists
|
|
56
|
-
self.value = value
|
|
58
|
+
self.exists: bool = exists
|
|
59
|
+
self.value: Any = value
|
|
57
60
|
|
|
58
61
|
def __bool__(self):
|
|
59
62
|
return self.exists
|
|
60
63
|
|
|
61
64
|
|
|
62
65
|
class Args:
|
|
63
|
-
"""
|
|
66
|
+
"""Container for parsed command-line arguments, allowing attribute-style access.
|
|
67
|
+
For example, if an argument `foo` was parsed, it can be accessed via `args.foo`.
|
|
68
|
+
Each such attribute (e.g. `args.foo`) is an instance of `ArgResult`."""
|
|
64
69
|
|
|
65
|
-
def __init__(self, **kwargs):
|
|
66
|
-
for
|
|
67
|
-
if not
|
|
68
|
-
raise TypeError(f"Argument alias '{
|
|
69
|
-
|
|
70
|
+
def __init__(self, **kwargs: dict[str, Any]):
|
|
71
|
+
for alias_name, data_dict in kwargs.items():
|
|
72
|
+
if not alias_name.isidentifier():
|
|
73
|
+
raise TypeError(f"Argument alias '{alias_name}' is invalid. It must be a valid Python variable name.")
|
|
74
|
+
arg_result_instance = ArgResult(exists=data_dict["exists"], value=data_dict["value"])
|
|
75
|
+
setattr(self, alias_name, arg_result_instance)
|
|
70
76
|
|
|
71
77
|
def __len__(self):
|
|
72
78
|
return len(vars(self))
|
|
@@ -74,6 +80,9 @@ class Args:
|
|
|
74
80
|
def __contains__(self, key):
|
|
75
81
|
return hasattr(self, key)
|
|
76
82
|
|
|
83
|
+
def __getattr__(self, name: str) -> ArgResult:
|
|
84
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
85
|
+
|
|
77
86
|
def __getitem__(self, key):
|
|
78
87
|
if isinstance(key, int):
|
|
79
88
|
return list(self.__iter__())[key]
|
|
@@ -108,14 +117,13 @@ class Console:
|
|
|
108
117
|
h: int = _ConsoleHeight() # type: ignore[assignment]
|
|
109
118
|
"""The height of the console in lines."""
|
|
110
119
|
wh: tuple[int, int] = _ConsoleSize() # type: ignore[assignment]
|
|
111
|
-
"""A tuple with the width and height of
|
|
112
|
-
the console in characters and lines."""
|
|
120
|
+
"""A tuple with the width and height of the console in characters and lines."""
|
|
113
121
|
usr: str = _ConsoleUser() # type: ignore[assignment]
|
|
114
122
|
"""The name of the current user."""
|
|
115
123
|
|
|
116
124
|
@staticmethod
|
|
117
125
|
def get_args(
|
|
118
|
-
find_args:
|
|
126
|
+
find_args: Mapping[str, list[str] | tuple[str, ...] | dict[str, list[str] | tuple[str, ...] | Any]],
|
|
119
127
|
allow_spaces: bool = False
|
|
120
128
|
) -> Args:
|
|
121
129
|
"""Will search for the specified arguments in the command line
|
|
@@ -267,7 +275,11 @@ class Console:
|
|
|
267
275
|
information about formatting codes, see `xx_format_codes` module documentation."""
|
|
268
276
|
title = "" if title is None else title.strip().upper()
|
|
269
277
|
title_len, tab_len = len(title) + 4, _console_tabsize - ((len(title) + 4) % _console_tabsize)
|
|
270
|
-
|
|
278
|
+
if title_bg_color is not None and Color.is_valid(title_bg_color):
|
|
279
|
+
title_bg_color = Color.to_hexa(title_bg_color)
|
|
280
|
+
title_color = Color.text_color_for_on_bg(title_bg_color)
|
|
281
|
+
else:
|
|
282
|
+
title_color = "_color" if title_bg_color is None else "#000"
|
|
271
283
|
if format_linebreaks:
|
|
272
284
|
clean_prompt, removals = FormatCodes.remove_formatting(str(prompt), get_removals=True, _ignore_linebreaks=True)
|
|
273
285
|
prompt_lst = (String.split_count(l, Console.w - (title_len + tab_len)) for l in str(clean_prompt).splitlines())
|
|
@@ -275,7 +287,7 @@ class Console:
|
|
|
275
287
|
item for lst in prompt_lst for item in ([""] if lst == [] else (lst if isinstance(lst, list) else [lst]))
|
|
276
288
|
)
|
|
277
289
|
prompt = f"\n{' ' * title_len}\t".join(
|
|
278
|
-
Console.__add_back_removed_parts(list(prompt_lst),
|
|
290
|
+
Console.__add_back_removed_parts(list(prompt_lst), cast(tuple[tuple[int, str], ...], removals))
|
|
279
291
|
)
|
|
280
292
|
else:
|
|
281
293
|
prompt = str(prompt)
|
|
@@ -427,18 +439,18 @@ class Console:
|
|
|
427
439
|
Console.pause_exit(pause, exit, reset_ansi=reset_ansi)
|
|
428
440
|
|
|
429
441
|
@staticmethod
|
|
430
|
-
def
|
|
442
|
+
def log_box_filled(
|
|
431
443
|
*values: object,
|
|
432
444
|
start: str = "",
|
|
433
445
|
end: str = "\n",
|
|
434
|
-
box_bg_color:
|
|
446
|
+
box_bg_color: Rgba | Hexa = "green",
|
|
435
447
|
default_color: Rgba | Hexa = "#000",
|
|
436
448
|
w_padding: int = 2,
|
|
437
449
|
w_full: bool = False,
|
|
438
450
|
) -> None:
|
|
439
|
-
"""Will print a box, containing a formatted log message:
|
|
451
|
+
"""Will print a box with a colored background, containing a formatted log message:
|
|
440
452
|
- `*values` -⠀the box content (each value is on a new line)
|
|
441
|
-
- `start` -⠀something to print before the log box is printed
|
|
453
|
+
- `start` -⠀something to print before the log box is printed (e.g. `\\n`)
|
|
442
454
|
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
443
455
|
- `box_bg_color` -⠀the background color of the box
|
|
444
456
|
- `default_color` -⠀the default text color of the `*values`
|
|
@@ -447,24 +459,101 @@ class Console:
|
|
|
447
459
|
-----------------------------------------------------------------------------------
|
|
448
460
|
The box content can be formatted with special formatting codes. For more detailed
|
|
449
461
|
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)
|
|
462
|
+
lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
|
|
453
463
|
pad_w_full = (Console.w - (max_line_len + (2 * w_padding))) if w_full else 0
|
|
464
|
+
if box_bg_color is not None and Color.is_valid(box_bg_color):
|
|
465
|
+
box_bg_color = Color.to_hexa(box_bg_color)
|
|
454
466
|
lines = [
|
|
455
467
|
f"[bg:{box_bg_color}]{' ' * w_padding}{line}" + " " *
|
|
456
|
-
((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[
|
|
468
|
+
((w_padding + max_line_len - len(unfmt)) + pad_w_full) + "[*]" for line, unfmt in zip(lines, unfmt_lines)
|
|
457
469
|
]
|
|
458
470
|
pady = " " * (Console.w if w_full else max_line_len + (2 * w_padding))
|
|
459
471
|
FormatCodes.print(
|
|
460
|
-
f"{start}[bg:{box_bg_color}]{pady}[
|
|
472
|
+
f"{start}[bg:{box_bg_color}]{pady}[*]\n"
|
|
461
473
|
+ _COMPILED["formatting"].sub(lambda m: f"{m.group(0)}[bg:{box_bg_color}]", "\n".join(lines))
|
|
462
|
-
+ f"\n[bg:{box_bg_color}]{pady}[
|
|
474
|
+
+ f"\n[bg:{box_bg_color}]{pady}[_]",
|
|
475
|
+
default_color=default_color or "#000",
|
|
476
|
+
sep="\n",
|
|
477
|
+
end=end,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
@staticmethod
|
|
481
|
+
def log_box_bordered(
|
|
482
|
+
*values: object,
|
|
483
|
+
start: str = "",
|
|
484
|
+
end: str = "\n",
|
|
485
|
+
border_type: Literal["standard", "rounded", "strong", "double"] = "rounded",
|
|
486
|
+
border_style: str | Rgba | Hexa = f"dim|{COLOR.gray}",
|
|
487
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
488
|
+
w_padding: int = 1,
|
|
489
|
+
w_full: bool = False,
|
|
490
|
+
_border_chars: Optional[tuple[str, str, str, str, str, str, str, str]] = None,
|
|
491
|
+
) -> None:
|
|
492
|
+
"""Will print a bordered box, containing a formatted log message:
|
|
493
|
+
- `*values` -⠀the box content (each value is on a new line)
|
|
494
|
+
- `start` -⠀something to print before the log box is printed (e.g. `\\n`)
|
|
495
|
+
- `end` -⠀something to print after the log box is printed (e.g. `\\n`)
|
|
496
|
+
- `border_type` -⠀one of the predefined border character sets
|
|
497
|
+
- `default_color` -⠀the default text color of the `*values`
|
|
498
|
+
- `w_padding` -⠀the horizontal padding (in chars) to the box content
|
|
499
|
+
- `w_full` -⠀whether to make the box be the full console width or not
|
|
500
|
+
- `_border_chars` -⠀define your own border characters set (overwrites `border_type`)\n
|
|
501
|
+
---------------------------------------------------------------------------------------
|
|
502
|
+
The box content can be formatted with special formatting codes. For more detailed
|
|
503
|
+
information about formatting codes, see `xx_format_codes` module documentation.\n
|
|
504
|
+
---------------------------------------------------------------------------------------
|
|
505
|
+
The `border_type` can be one of the following:
|
|
506
|
+
- `"standard" = ('┌', '─', '┐', '│', '┘', '─', '└', '│')`
|
|
507
|
+
- `"rounded" = ('╭', '─', '╮', '│', '╯', '─', '╰', '│')`
|
|
508
|
+
- `"strong" = ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃')`
|
|
509
|
+
- `"double" = ('╔', '═', '╗', '║', '╝', '═', '╚', '║')`\n
|
|
510
|
+
The order of the characters is always:
|
|
511
|
+
1. top-left corner
|
|
512
|
+
2. top border
|
|
513
|
+
3. top-right corner
|
|
514
|
+
4. right border
|
|
515
|
+
5. bottom-right corner
|
|
516
|
+
6. bottom border
|
|
517
|
+
7. bottom-left corner
|
|
518
|
+
8. left border"""
|
|
519
|
+
borders = {
|
|
520
|
+
"standard": ('┌', '─', '┐', '│', '┘', '─', '└', '│'),
|
|
521
|
+
"rounded": ('╭', '─', '╮', '│', '╯', '─', '╰', '│'),
|
|
522
|
+
"strong": ('┏', '━', '┓', '┃', '┛', '━', '┗', '┃'),
|
|
523
|
+
"double": ('╔', '═', '╗', '║', '╝', '═', '╚', '║'),
|
|
524
|
+
}
|
|
525
|
+
border_chars = borders.get(border_type, borders["standard"]) if _border_chars is None else _border_chars
|
|
526
|
+
lines, unfmt_lines, max_line_len = Console.__prepare_log_box(values, default_color)
|
|
527
|
+
print(unfmt_lines)
|
|
528
|
+
pad_w_full = (Console.w - (max_line_len + (2 * w_padding)) - (len(border_chars[1] * 2))) if w_full else 0
|
|
529
|
+
if border_style is not None and Color.is_valid(border_style):
|
|
530
|
+
border_style = Color.to_hexa(border_style)
|
|
531
|
+
border_l = f"[{border_style}]{border_chars[7]}[*]"
|
|
532
|
+
border_r = f"[{border_style}]{border_chars[3]}[_]"
|
|
533
|
+
lines = [
|
|
534
|
+
f"{border_l}{' ' * w_padding}{line}[_]" + " " * ((w_padding + max_line_len - len(unfmt)) + pad_w_full) + border_r
|
|
535
|
+
for line, unfmt in zip(lines, unfmt_lines)
|
|
536
|
+
]
|
|
537
|
+
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]}[_]"
|
|
538
|
+
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]}[_]"
|
|
539
|
+
FormatCodes.print(
|
|
540
|
+
f"{start}{border_t}[_]\n" + "\n".join(lines) + f"\n{border_b}[_]",
|
|
463
541
|
default_color=default_color,
|
|
464
542
|
sep="\n",
|
|
465
543
|
end=end,
|
|
466
544
|
)
|
|
467
545
|
|
|
546
|
+
@staticmethod
|
|
547
|
+
def __prepare_log_box(
|
|
548
|
+
values: tuple[object, ...],
|
|
549
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
550
|
+
) -> tuple[list[str], list[tuple[str, tuple[tuple[int, str], ...]]], int]:
|
|
551
|
+
"""Prepares the log box content and returns it along with the max line length."""
|
|
552
|
+
lines = [line for val in values for line in str(val).splitlines()]
|
|
553
|
+
unfmt_lines = [FormatCodes.remove_formatting(line, default_color) for line in lines]
|
|
554
|
+
max_line_len = max(len(line) for line in unfmt_lines)
|
|
555
|
+
return lines, cast(list[tuple[str, tuple[tuple[int, str], ...]]], unfmt_lines), max_line_len
|
|
556
|
+
|
|
468
557
|
@staticmethod
|
|
469
558
|
def confirm(
|
|
470
559
|
prompt: object = "Do you want to continue?",
|
|
@@ -494,7 +583,7 @@ class Console:
|
|
|
494
583
|
end="\n",
|
|
495
584
|
default_color: Rgba | Hexa = COLOR.cyan,
|
|
496
585
|
show_keybindings=True,
|
|
497
|
-
input_prefix="
|
|
586
|
+
input_prefix=" ⮡ ",
|
|
498
587
|
reset_ansi=True,
|
|
499
588
|
) -> str:
|
|
500
589
|
"""An input where users can input (and paste) text over multiple lines.\n
|
xulbux/xx_format_codes.py
CHANGED
|
@@ -133,7 +133,8 @@ 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` it resets everything, including the text color*)
|
|
137
|
+
2. `[*color]` `[*c]` will reset the text color, just like `[_color]`, but then also make it `default_color`
|
|
137
138
|
3. `[default]` will just color the text in `default_color`
|
|
138
139
|
4. `[background:default]` `[BG:default]` will color the background in `default_color`
|
|
139
140
|
|
|
@@ -155,7 +156,7 @@ from .xx_string import String
|
|
|
155
156
|
from .xx_regex import Regex, Match, Pattern
|
|
156
157
|
from .xx_color import Color, rgba, Rgba, Hexa
|
|
157
158
|
|
|
158
|
-
from typing import Optional
|
|
159
|
+
from typing import Optional, cast
|
|
159
160
|
import ctypes as _ctypes
|
|
160
161
|
import regex as _rx
|
|
161
162
|
import sys as _sys
|
|
@@ -174,7 +175,7 @@ _PREFIX_RX: dict[str, str] = {
|
|
|
174
175
|
}
|
|
175
176
|
_COMPILED: dict[str, Pattern] = { # PRECOMPILE REGULAR EXPRESSIONS
|
|
176
177
|
"*": _re.compile(r"\[\s*([^]_]*?)\s*\*\s*([^]_]*?)\]"),
|
|
177
|
-
"*color": _re.compile(r"\[\s*([^]_]*?)\s*\*
|
|
178
|
+
"*color": _re.compile(r"\[\s*([^]_]*?)\s*\*c(?:olor)?\s*([^]_]*?)\]"),
|
|
178
179
|
"ansi_seq": _re.compile(ANSI.char + r"(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])"),
|
|
179
180
|
"formatting": _rx.compile(
|
|
180
181
|
Regex.brackets("[", "]", is_group=True, ignore_in_strings=False)
|
|
@@ -253,15 +254,18 @@ class FormatCodes:
|
|
|
253
254
|
`xx_format_codes` module documentation."""
|
|
254
255
|
if not isinstance(string, str):
|
|
255
256
|
string = str(string)
|
|
256
|
-
if default_color and Color.is_valid_rgba(default_color, False):
|
|
257
|
+
if default_color and Color.is_valid_rgba(default_color, False):
|
|
257
258
|
use_default = True
|
|
258
|
-
elif default_color and Color.is_valid_hexa(default_color, False):
|
|
259
|
+
elif default_color and Color.is_valid_hexa(default_color, False):
|
|
259
260
|
use_default, default_color = True, Color.to_rgba(default_color)
|
|
260
261
|
else:
|
|
261
262
|
use_default = False
|
|
263
|
+
default_color = cast(rgba, default_color) if use_default else None
|
|
262
264
|
if use_default:
|
|
263
265
|
string = _COMPILED["*"].sub(r"[\1_|default\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|default|…]`
|
|
264
|
-
string = _COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` WITH `[…|default|…]`
|
|
266
|
+
string = _COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` OR `[…|*c|…]` WITH `[…|default|…]`
|
|
267
|
+
else:
|
|
268
|
+
string = _COMPILED["*"].sub(r"[\1_\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|…]`
|
|
265
269
|
|
|
266
270
|
def is_valid_color(color: str) -> bool:
|
|
267
271
|
return bool((color in ANSI.color_map) or Color.is_valid_rgba(color) or Color.is_valid_hexa(color))
|
|
@@ -280,8 +284,8 @@ class FormatCodes:
|
|
|
280
284
|
formats = FormatCodes.to_ansi(formats, default_color, brightness_steps, False)
|
|
281
285
|
format_keys = [k.strip() for k in formats.split("|") if k.strip()]
|
|
282
286
|
ansi_formats = [
|
|
283
|
-
r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k
|
|
284
|
-
|
|
287
|
+
r if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)) != k else f"[{k}]"
|
|
288
|
+
for k in format_keys
|
|
285
289
|
]
|
|
286
290
|
if auto_reset_txt and not auto_reset_escaped:
|
|
287
291
|
reset_keys = []
|
|
@@ -292,7 +296,7 @@ class FormatCodes:
|
|
|
292
296
|
if k_set & _PREFIX["BR"]:
|
|
293
297
|
for i in range(len(k)):
|
|
294
298
|
if is_valid_color(k[i:]):
|
|
295
|
-
reset_keys.extend(["_bg", "
|
|
299
|
+
reset_keys.extend(["_bg", "default"] if use_default else ["_bg", "_c"])
|
|
296
300
|
break
|
|
297
301
|
else:
|
|
298
302
|
for i in range(len(k)):
|
|
@@ -302,13 +306,12 @@ class FormatCodes:
|
|
|
302
306
|
elif is_valid_color(k) or any(
|
|
303
307
|
k_lower.startswith(pref_colon := f"{prefix}:") and is_valid_color(k[len(pref_colon):])
|
|
304
308
|
for prefix in _PREFIX["BR"]):
|
|
305
|
-
reset_keys.append("
|
|
309
|
+
reset_keys.append("default" if use_default else "_c")
|
|
306
310
|
else:
|
|
307
311
|
reset_keys.append(f"_{k}")
|
|
308
312
|
ansi_resets = [
|
|
309
|
-
r for k in reset_keys
|
|
310
|
-
|
|
311
|
-
).startswith(f"{ANSI.char}{ANSI.start}")
|
|
313
|
+
r for k in reset_keys if (r := FormatCodes.__get_replacement(k, default_color, brightness_steps)
|
|
314
|
+
).startswith(f"{ANSI.char}{ANSI.start}")
|
|
312
315
|
]
|
|
313
316
|
else:
|
|
314
317
|
ansi_resets = []
|
|
@@ -326,8 +329,8 @@ class FormatCodes:
|
|
|
326
329
|
)
|
|
327
330
|
|
|
328
331
|
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
|
|
332
|
+
return (((FormatCodes.__get_default_ansi(default_color.values()) or "") if _default_start else "")
|
|
333
|
+
+ string) if default_color is not None else string
|
|
331
334
|
|
|
332
335
|
@staticmethod
|
|
333
336
|
def escape_ansi(ansi_string: str) -> str:
|
|
@@ -366,6 +369,7 @@ class FormatCodes:
|
|
|
366
369
|
@staticmethod
|
|
367
370
|
def remove_formatting(
|
|
368
371
|
string: str,
|
|
372
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
369
373
|
get_removals: bool = False,
|
|
370
374
|
_ignore_linebreaks: bool = False,
|
|
371
375
|
) -> str | tuple[str, tuple[tuple[int, str], ...]]:
|
|
@@ -375,7 +379,7 @@ class FormatCodes:
|
|
|
375
379
|
Each tuple contains the position of the removed formatting code and the removed formatting code.\n
|
|
376
380
|
If `_ignore_linebreaks` is true, linebreaks will be ignored for the removal positions."""
|
|
377
381
|
return FormatCodes.remove_ansi(
|
|
378
|
-
FormatCodes.to_ansi(string),
|
|
382
|
+
FormatCodes.to_ansi(string, default_color=default_color),
|
|
379
383
|
get_removals=get_removals,
|
|
380
384
|
_ignore_linebreaks=_ignore_linebreaks,
|
|
381
385
|
)
|
|
@@ -395,7 +399,7 @@ class FormatCodes:
|
|
|
395
399
|
|
|
396
400
|
@staticmethod
|
|
397
401
|
def __get_default_ansi(
|
|
398
|
-
default_color: tuple,
|
|
402
|
+
default_color: tuple[int, int, int],
|
|
399
403
|
format_key: Optional[str] = None,
|
|
400
404
|
brightness_steps: Optional[int] = None,
|
|
401
405
|
_modifiers: tuple[str, str] = (ANSI.default_color_modifiers["lighten"], ANSI.default_color_modifiers["darken"]),
|
|
@@ -428,12 +432,12 @@ class FormatCodes:
|
|
|
428
432
|
return (ANSI.seq_bg_color if is_bg else ANSI.seq_color).format(*new_rgb[:3])
|
|
429
433
|
|
|
430
434
|
@staticmethod
|
|
431
|
-
def __get_replacement(format_key: str, default_color: Optional[
|
|
435
|
+
def __get_replacement(format_key: str, default_color: Optional[rgba], brightness_steps: int = 20) -> str:
|
|
432
436
|
"""Gives you the corresponding ANSI code for the given format key.
|
|
433
437
|
If `default_color` is not `None`, the text color will be `default_color` if all formats
|
|
434
438
|
are reset or you can get lighter or darker version of `default_color` (also as BG)"""
|
|
435
439
|
use_default = default_color and Color.is_valid_rgba(default_color, False)
|
|
436
|
-
_default_color = tuple(Color.to_rgba(default_color)) if
|
|
440
|
+
_default_color = tuple(Color.to_rgba(default_color)) if default_color is not None else tuple()
|
|
437
441
|
_format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
|
|
438
442
|
if use_default:
|
|
439
443
|
if new_default_color := FormatCodes.__get_default_ansi(_default_color, format_key, brightness_steps):
|
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.1
|
|
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/)
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
xulbux/__init__.py,sha256=
|
|
1
|
+
xulbux/__init__.py,sha256=4qceHbARrYX-ui-kl5bufH3BlEvMwJpsXG1TQsbNHgA,815
|
|
2
2
|
xulbux/_cli_.py,sha256=J4vfJHLJEYxCZzA_VJUB46w2WGShfdYFoetsLG5PfKo,3428
|
|
3
3
|
xulbux/_consts_.py,sha256=AuYTTmqrP2lawyVGlPLUaP1syxOoPA-ejJGH7WlwFzk,6314
|
|
4
4
|
xulbux/xx_code.py,sha256=w9yO-GPMeaE-xDi-L3VtpPpWpu5jOwagfMsG93aXANE,6106
|
|
5
|
-
xulbux/xx_color.py,sha256=
|
|
6
|
-
xulbux/xx_console.py,sha256=
|
|
5
|
+
xulbux/xx_color.py,sha256=Thj7fFTc8x-VmYyULI3HQW0uCk9dIihwrGUwuXGn83s,49744
|
|
6
|
+
xulbux/xx_console.py,sha256=mruEcbehVceXuDhIMV_WoFec8MxXf2orvlNQWkdI-zE,34367
|
|
7
7
|
xulbux/xx_data.py,sha256=nEfVwK6-ILaL3K-bLezKpG1G7117CY5ZgC3BGwANrUE,30886
|
|
8
8
|
xulbux/xx_env_path.py,sha256=x56mKK4lSvU5yMCAs8k0RVIqXWUJcpcHYz5HoZ_RklM,4160
|
|
9
9
|
xulbux/xx_file.py,sha256=KerXOvKS93zIoAt36YTYuZboSmxVFVf2WcOrDcdwXfE,2627
|
|
10
|
-
xulbux/xx_format_codes.py,sha256=
|
|
10
|
+
xulbux/xx_format_codes.py,sha256=NihbRkPjDSWJCL2itZBlvhbO0bWx8Mm2P-LpSeaG3po,23732
|
|
11
11
|
xulbux/xx_json.py,sha256=V7vdfpvSe9wpktR_c8zG_Meix7x9IRmn66k5nB3HUyo,7457
|
|
12
12
|
xulbux/xx_path.py,sha256=lLAEVZrW0TAwCewlONFVQcQ_8tVn9LTJZVOZpeGvE5s,7673
|
|
13
|
-
xulbux/xx_regex.py,sha256=
|
|
13
|
+
xulbux/xx_regex.py,sha256=_BtMHRDNcD9zF4SL87dQuUVZcYGfZx9H5YNSDiEtzm8,8059
|
|
14
14
|
xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
|
|
15
15
|
xulbux/xx_system.py,sha256=Tsx4wgztUg46KloqcGeiFkarDoM3EgJLXw3XNxgHBmU,6460
|
|
16
|
-
xulbux-1.7.
|
|
17
|
-
xulbux-1.7.
|
|
18
|
-
xulbux-1.7.
|
|
19
|
-
xulbux-1.7.
|
|
20
|
-
xulbux-1.7.
|
|
21
|
-
xulbux-1.7.
|
|
16
|
+
xulbux-1.7.1.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
|
|
17
|
+
xulbux-1.7.1.dist-info/METADATA,sha256=rMdO8mHMOtT-oLsysIUup9myW_B93Z2UdG1Gvasu_rA,9209
|
|
18
|
+
xulbux-1.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
xulbux-1.7.1.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
|
|
20
|
+
xulbux-1.7.1.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
|
|
21
|
+
xulbux-1.7.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|