xulbux 1.6.7__py3-none-any.whl → 1.6.9__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 +4 -36
- xulbux/_cli_.py +4 -4
- xulbux/xx_code.py +58 -43
- xulbux/xx_color.py +91 -58
- xulbux/xx_console.py +176 -40
- xulbux/xx_data.py +60 -53
- xulbux/xx_env_path.py +3 -19
- xulbux/xx_file.py +18 -30
- xulbux/xx_format_codes.py +60 -17
- xulbux/xx_json.py +105 -50
- xulbux/xx_path.py +71 -21
- xulbux/xx_regex.py +6 -14
- xulbux/xx_string.py +4 -1
- xulbux/xx_system.py +1 -5
- {xulbux-1.6.7.dist-info → xulbux-1.6.9.dist-info}/METADATA +19 -40
- xulbux-1.6.9.dist-info/RECORD +21 -0
- {xulbux-1.6.7.dist-info → xulbux-1.6.9.dist-info}/WHEEL +1 -1
- xulbux-1.6.7.dist-info/RECORD +0 -21
- {xulbux-1.6.7.dist-info → xulbux-1.6.9.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.7.dist-info → xulbux-1.6.9.dist-info/licenses}/LICENSE +0 -0
- {xulbux-1.6.7.dist-info → xulbux-1.6.9.dist-info}/top_level.txt +0 -0
xulbux/__init__.py
CHANGED
|
@@ -1,45 +1,13 @@
|
|
|
1
|
-
""
|
|
2
|
-
>>> import xulbux as xx
|
|
3
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
4
|
-
• CUSTOM TYPES:
|
|
5
|
-
• rgba(int,int,int,float)
|
|
6
|
-
• hsla(int,int,int,float)
|
|
7
|
-
• hexa(str)
|
|
8
|
-
• PATH OPERATIONS xx.Path
|
|
9
|
-
• FILE OPERATIONS xx.File
|
|
10
|
-
• JSON FILE OPERATIONS xx.Json
|
|
11
|
-
• SYSTEM ACTIONS xx.System
|
|
12
|
-
• MANAGE THE ENV PATH VAR xx.EnvPath
|
|
13
|
-
• CONSOLE LOG AND ACTIONS xx.Console
|
|
14
|
-
• EASY PRETTY PRINTING xx.FormatCodes
|
|
15
|
-
• WORKING WITH COLORS xx.Color
|
|
16
|
-
• DATA OPERATIONS xx.Data
|
|
17
|
-
• STRING OPERATIONS xx.String
|
|
18
|
-
• CODE STRING OPERATIONS xx.Code
|
|
19
|
-
• REGEX PATTERN TEMPLATES xx.Regex
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
__version__ = "1.6.7"
|
|
1
|
+
__version__ = "1.6.9"
|
|
23
2
|
__author__ = "XulbuX"
|
|
24
3
|
__email__ = "xulbux.real@gmail.com"
|
|
25
4
|
__license__ = "MIT"
|
|
26
|
-
__copyright__ = "Copyright (c)
|
|
5
|
+
__copyright__ = "Copyright (c) 2025 XulbuX"
|
|
27
6
|
__url__ = "https://github.com/XulbuX/PythonLibraryXulbuX"
|
|
28
7
|
__description__ = "A library which includes a lot of really helpful functions."
|
|
29
8
|
__all__ = [
|
|
30
|
-
"_consts_",
|
|
31
|
-
"
|
|
32
|
-
"xx_code",
|
|
33
|
-
"xx_color",
|
|
34
|
-
"xx_data",
|
|
35
|
-
"xx_env_path",
|
|
36
|
-
"xx_file",
|
|
37
|
-
"xx_format_codes",
|
|
38
|
-
"xx_json",
|
|
39
|
-
"xx_path",
|
|
40
|
-
"xx_regex",
|
|
41
|
-
"xx_string",
|
|
42
|
-
"xx_system",
|
|
9
|
+
"_consts_", "xx_code", "xx_color", "xx_console", "xx_data", "xx_env_path", "xx_file", "xx_format_codes", "xx_json",
|
|
10
|
+
"xx_path", "xx_regex", "xx_string", "xx_system"
|
|
43
11
|
]
|
|
44
12
|
|
|
45
13
|
from ._consts_ import *
|
xulbux/_cli_.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from . import __version__
|
|
2
1
|
from ._consts_ import COLOR
|
|
2
|
+
from . import __version__
|
|
3
3
|
from .xx_format_codes import FormatCodes
|
|
4
4
|
from .xx_console import Console
|
|
5
5
|
|
|
@@ -33,17 +33,17 @@ def help_command():
|
|
|
33
33
|
[dim](•) [{color['class']}]rgba[{color['punctuators']}]/([i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]float[_|{color['punctuators']}])[*]
|
|
34
34
|
[dim](•) [{color['class']}]hsla[{color['punctuators']}]/([i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]float[_|{color['punctuators']}])[*]
|
|
35
35
|
[dim](•) [{color['class']}]hexa[{color['punctuators']}]/([i|{color['types']}]str[_|{color['punctuators']}]|[i|{color['types']}]int[_|{color['punctuators']}])[*]
|
|
36
|
+
[dim](•) CODE STRING OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Code[*]
|
|
37
|
+
[dim](•) WORKING WITH COLORS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Color[*]
|
|
38
|
+
[dim](•) CONSOLE LOG AND ACTIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Console[*]
|
|
36
39
|
[dim](•) PATH OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Path[*]
|
|
37
40
|
[dim](•) FILE OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]File[*]
|
|
38
41
|
[dim](•) JSON FILE OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Json[*]
|
|
39
42
|
[dim](•) SYSTEM ACTIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]System[*]
|
|
40
43
|
[dim](•) MANAGE THE ENV PATH VAR [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]EnvPath[*]
|
|
41
|
-
[dim](•) CONSOLE LOG AND ACTIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Console[*]
|
|
42
44
|
[dim](•) EASY PRETTY PRINTING [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]FormatCodes[*]
|
|
43
|
-
[dim](•) WORKING WITH COLORS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Color[*]
|
|
44
45
|
[dim](•) DATA OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Data[*]
|
|
45
46
|
[dim](•) STRING OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]String[*]
|
|
46
|
-
[dim](•) CODE STRING OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Code[*]
|
|
47
47
|
[dim](•) REGEX PATTERN TEMPLATES [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Regex[*]
|
|
48
48
|
[_]
|
|
49
49
|
[dim](Press any key to exit...)
|
xulbux/xx_code.py
CHANGED
|
@@ -55,46 +55,61 @@ class Code:
|
|
|
55
55
|
@staticmethod
|
|
56
56
|
def is_js(code: str, funcs: list = ["__", "$t", "$lang"]) -> bool:
|
|
57
57
|
"""Will check if the code is very likely to be JavaScript."""
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
58
|
+
if not code or len(code.strip()) < 3:
|
|
59
|
+
return False
|
|
60
|
+
for func in funcs:
|
|
61
|
+
if _rx.match(r"^[\s\n]*" + _rx.escape(func) + r"\([^\)]*\)[\s\n]*$", code):
|
|
62
|
+
return True
|
|
63
|
+
direct_js_patterns = [
|
|
64
|
+
r"^[\s\n]*\$\(['\"][^'\"]+['\"]\)\.[\w]+\([^\)]*\);?[\s\n]*$", # jQuery calls
|
|
65
|
+
r"^[\s\n]*\$\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # $.ajax(), etc.
|
|
66
|
+
r"^[\s\n]*\(\s*function\s*\(\)\s*\{.*\}\s*\)\(\);?[\s\n]*$", # IIFE
|
|
67
|
+
r"^[\s\n]*document\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # document.getElementById()
|
|
68
|
+
r"^[\s\n]*window\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # window.alert()
|
|
69
|
+
r"^[\s\n]*console\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # console.log()
|
|
70
|
+
]
|
|
71
|
+
for pattern in direct_js_patterns:
|
|
72
|
+
if _rx.match(pattern, code):
|
|
73
|
+
return True
|
|
74
|
+
arrow_function_patterns = [
|
|
75
|
+
r"^[\s\n]*\b[\w_]+\s*=\s*\([^\)]*\)\s*=>\s*[^;{]*[;]?[\s\n]*$", # const x = (y) => y*2;
|
|
76
|
+
r"^[\s\n]*\b[\w_]+\s*=\s*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$", # const x = y => y*2;
|
|
77
|
+
r"^[\s\n]*\(\s*[\w_,\s]+\s*\)\s*=>\s*[^;{]*[;]?[\s\n]*$", # (x) => x*2
|
|
78
|
+
r"^[\s\n]*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$", # x => x*2
|
|
79
|
+
]
|
|
80
|
+
for pattern in arrow_function_patterns:
|
|
81
|
+
if _rx.match(pattern, code):
|
|
82
|
+
return True
|
|
83
|
+
funcs_pattern = r"(" + "|".join(_rx.escape(f) for f in funcs) + r")" + Regex.brackets("()")
|
|
84
|
+
js_indicators = [(r"\b(var|let|const)\s+[\w_$]+", 2), # JS variable declarations
|
|
85
|
+
(r"\$[\w_$]+\s*=", 2), # jQuery-style variables
|
|
86
|
+
(r"\$[\w_$]+\s*\(", 2), # jQuery function calls
|
|
87
|
+
(r"\bfunction\s*[\w_$]*\s*\(", 2), # Function declarations
|
|
88
|
+
(r"[\w_$]+\s*=\s*function\s*\(", 2), # Function assignments
|
|
89
|
+
(r"\b[\w_$]+\s*=>\s*[\{\(]", 2), # Arrow functions
|
|
90
|
+
(r"\(function\s*\(\)\s*\{", 2), # IIFE pattern
|
|
91
|
+
(funcs_pattern, 2), # Custom predefined functions
|
|
92
|
+
(r"\b(true|false|null|undefined)\b", 1), # JS literals
|
|
93
|
+
(r"===|!==|\+\+|--|\|\||&&", 1.5), # JS-specific operators
|
|
94
|
+
(r"\bnew\s+[\w_$]+\s*\(", 1.5), # Object instantiation with new
|
|
95
|
+
(r"\b(document|window|console|Math|Array|Object|String|Number)\.", 2), # JS objects
|
|
96
|
+
(r"\basync\s+function|\bawait\b", 2), # Async/await
|
|
97
|
+
(r"\b(if|for|while|switch)\s*\([^)]*\)\s*\{", 1), # Control structures with braces
|
|
98
|
+
(r"\btry\s*\{[^}]*\}\s*catch\s*\(", 1.5), # Try-catch
|
|
99
|
+
(r";[\s\n]*$", 0.5), # Semicolon line endings
|
|
100
|
+
]
|
|
101
|
+
js_score = 0
|
|
102
|
+
line_endings = [line.strip() for line in code.splitlines() if line.strip()]
|
|
103
|
+
semicolon_endings = sum(1 for line in line_endings if line.endswith(';'))
|
|
104
|
+
if semicolon_endings >= 1:
|
|
105
|
+
js_score += min(semicolon_endings, 2)
|
|
106
|
+
opening_braces = code.count('{')
|
|
107
|
+
closing_braces = code.count('}')
|
|
108
|
+
if opening_braces > 0 and opening_braces == closing_braces:
|
|
109
|
+
js_score += 1
|
|
110
|
+
for pattern, score in js_indicators:
|
|
111
|
+
regex = _rx.compile(pattern, _rx.IGNORECASE)
|
|
112
|
+
matches = regex.findall(code)
|
|
113
|
+
if matches:
|
|
114
|
+
js_score += len(matches) * score
|
|
115
|
+
return js_score >= 2
|
xulbux/xx_color.py
CHANGED
|
@@ -97,12 +97,7 @@ class rgba:
|
|
|
97
97
|
def __eq__(self, other: "rgba") -> bool:
|
|
98
98
|
if not isinstance(other, rgba):
|
|
99
99
|
return False
|
|
100
|
-
return (self.r, self.g, self.b, self.a) == (
|
|
101
|
-
other[0],
|
|
102
|
-
other[1],
|
|
103
|
-
other[2],
|
|
104
|
-
other[3],
|
|
105
|
-
)
|
|
100
|
+
return (self.r, self.g, self.b, self.a) == (other.r, other.g, other.b, other.a)
|
|
106
101
|
|
|
107
102
|
def dict(self) -> dict:
|
|
108
103
|
"""Returns the color components as a dictionary with keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
|
|
@@ -156,9 +151,15 @@ class rgba:
|
|
|
156
151
|
self.a = 1 - self.a
|
|
157
152
|
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
158
153
|
|
|
159
|
-
def grayscale(self) -> "rgba":
|
|
160
|
-
"""Converts the color to grayscale using the luminance formula
|
|
161
|
-
|
|
154
|
+
def grayscale(self, method: str = "wcag2") -> "rgba":
|
|
155
|
+
"""Converts the color to grayscale using the luminance formula.\n
|
|
156
|
+
------------------------------------------------------------------
|
|
157
|
+
The `method` is the luminance calculation method to use:
|
|
158
|
+
- `"wcag2"` WCAG 2.0 standard (default and most accurate for perception)
|
|
159
|
+
- `"wcag3"` Draft WCAG 3.0 standard with improved coefficients
|
|
160
|
+
- `"simple"` Simple arithmetic mean (less accurate)
|
|
161
|
+
- `"bt601"` ITU-R BT.601 standard (older TV standard)"""
|
|
162
|
+
self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b, method=method)
|
|
162
163
|
return rgba(self.r, self.g, self.b, self.a, _validate=False)
|
|
163
164
|
|
|
164
165
|
def blend(self, other: "rgba", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
|
|
@@ -286,20 +287,15 @@ class hsla:
|
|
|
286
287
|
return ((self.h, self.s, self.l) + (() if self.a is None else (self.a, )))[index]
|
|
287
288
|
|
|
288
289
|
def __repr__(self) -> str:
|
|
289
|
-
return f'hsla({self.h}
|
|
290
|
+
return f'hsla({self.h}°, {self.s}%, {self.l}%{"" if self.a is None else f", {self.a}"})'
|
|
290
291
|
|
|
291
292
|
def __str__(self) -> str:
|
|
292
|
-
return f'({self.h}
|
|
293
|
+
return f'({self.h}°, {self.s}%, {self.l}%{"" if self.a is None else f", {self.a}"})'
|
|
293
294
|
|
|
294
295
|
def __eq__(self, other: "hsla") -> bool:
|
|
295
296
|
if not isinstance(other, hsla):
|
|
296
297
|
return False
|
|
297
|
-
return (self.h, self.s, self.l, self.a) == (
|
|
298
|
-
other[0],
|
|
299
|
-
other[1],
|
|
300
|
-
other[2],
|
|
301
|
-
other[3],
|
|
302
|
-
)
|
|
298
|
+
return (self.h, self.s, self.l, self.a) == (other.h, other.s, other.l, other.a)
|
|
303
299
|
|
|
304
300
|
def dict(self) -> dict:
|
|
305
301
|
"""Returns the color components as a dictionary with keys `'h'`, `'s'`, `'l'` and optionally `'a'`"""
|
|
@@ -363,9 +359,15 @@ class hsla:
|
|
|
363
359
|
self.a = 1 - self.a
|
|
364
360
|
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
365
361
|
|
|
366
|
-
def grayscale(self) -> "hsla":
|
|
367
|
-
"""Converts the color to grayscale using the luminance formula
|
|
368
|
-
|
|
362
|
+
def grayscale(self, method: str = "wcag2") -> "hsla":
|
|
363
|
+
"""Converts the color to grayscale using the luminance formula.\n
|
|
364
|
+
------------------------------------------------------------------
|
|
365
|
+
The `method` is the luminance calculation method to use:
|
|
366
|
+
- `"wcag2"` WCAG 2.0 standard (default and most accurate for perception)
|
|
367
|
+
- `"wcag3"` Draft WCAG 3.0 standard with improved coefficients
|
|
368
|
+
- `"simple"` Simple arithmetic mean (less accurate)
|
|
369
|
+
- `"bt601"` ITU-R BT.601 standard (older TV standard)"""
|
|
370
|
+
l = Color.luminance(*self._hsl_to_rgb(self.h, self.s, self.l), method=method)
|
|
369
371
|
self.h, self.s, self.l, _ = rgba(l, l, l, _validate=False).to_hsla().values()
|
|
370
372
|
return hsla(self.h, self.s, self.l, self.a, _validate=False)
|
|
371
373
|
|
|
@@ -520,14 +522,10 @@ class hexa:
|
|
|
520
522
|
return f'#{self.r:02X}{self.g:02X}{self.b:02X}{"" if self.a is None else f"{int(self.a * 255):02X}"}'
|
|
521
523
|
|
|
522
524
|
def __eq__(self, other: "hexa") -> bool:
|
|
525
|
+
"""Returns whether the other color is equal to this one."""
|
|
523
526
|
if not isinstance(other, hexa):
|
|
524
527
|
return False
|
|
525
|
-
return (self.r, self.g, self.b, self.a) == (
|
|
526
|
-
other[0],
|
|
527
|
-
other[1],
|
|
528
|
-
other[2],
|
|
529
|
-
other[3],
|
|
530
|
-
)
|
|
528
|
+
return (self.r, self.g, self.b, self.a) == (other.r, other.g, other.b, other.a)
|
|
531
529
|
|
|
532
530
|
def dict(self) -> dict:
|
|
533
531
|
"""Returns the color components as a dictionary with hex string values for keys `'r'`, `'g'`, `'b'` and optionally `'a'`"""
|
|
@@ -540,9 +538,9 @@ class hexa:
|
|
|
540
538
|
)
|
|
541
539
|
)
|
|
542
540
|
|
|
543
|
-
def values(self) -> tuple:
|
|
541
|
+
def values(self, round_alpha: bool = True) -> tuple:
|
|
544
542
|
"""Returns the color components as separate values `r, g, b, a`"""
|
|
545
|
-
return self.r, self.g, self.b, self.a
|
|
543
|
+
return self.r, self.g, self.b, None if self.a is None else (round(self.a, 2) if round_alpha else self.a)
|
|
546
544
|
|
|
547
545
|
def to_rgba(self, round_alpha: bool = True) -> "rgba":
|
|
548
546
|
"""Returns the color as a `rgba()` color"""
|
|
@@ -594,9 +592,15 @@ class hexa:
|
|
|
594
592
|
self.a = 1 - self.a
|
|
595
593
|
return hexa("", self.r, self.g, self.b, self.a)
|
|
596
594
|
|
|
597
|
-
def grayscale(self) -> "hexa":
|
|
598
|
-
"""Converts the color to grayscale using the luminance formula
|
|
599
|
-
|
|
595
|
+
def grayscale(self, method: str = "wcag2") -> "hexa":
|
|
596
|
+
"""Converts the color to grayscale using the luminance formula.\n
|
|
597
|
+
------------------------------------------------------------------
|
|
598
|
+
The `method` is the luminance calculation method to use:
|
|
599
|
+
- `"wcag2"` WCAG 2.0 standard (default and most accurate for perception)
|
|
600
|
+
- `"wcag3"` Draft WCAG 3.0 standard with improved coefficients
|
|
601
|
+
- `"simple"` Simple arithmetic mean (less accurate)
|
|
602
|
+
- `"bt601"` ITU-R BT.601 standard (older TV standard)"""
|
|
603
|
+
self.r = self.g = self.b = Color.luminance(self.r, self.g, self.b, method=method)
|
|
600
604
|
return hexa("", self.r, self.g, self.b, self.a)
|
|
601
605
|
|
|
602
606
|
def blend(self, other: "hexa", ratio: float = 0.5, additive_alpha: bool = False) -> "rgba":
|
|
@@ -647,14 +651,20 @@ class Color:
|
|
|
647
651
|
0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
|
|
648
652
|
and (0 <= color[3] <= 1 or color[3] is None)
|
|
649
653
|
)
|
|
650
|
-
|
|
654
|
+
elif len(color) == 3:
|
|
655
|
+
return 0 <= color[0] <= 255 and 0 <= color[1] <= 255 and 0 <= color[2] <= 255
|
|
656
|
+
else:
|
|
657
|
+
return False
|
|
651
658
|
elif isinstance(color, dict):
|
|
652
659
|
if allow_alpha and Color.has_alpha(color):
|
|
653
660
|
return (
|
|
654
661
|
0 <= color["r"] <= 255 and 0 <= color["g"] <= 255 and 0 <= color["b"] <= 255
|
|
655
662
|
and (0 <= color["a"] <= 1 or color["a"] is None)
|
|
656
663
|
)
|
|
657
|
-
|
|
664
|
+
elif len(color) == 3:
|
|
665
|
+
return 0 <= color["r"] <= 255 and 0 <= color["g"] <= 255 and 0 <= color["b"] <= 255
|
|
666
|
+
else:
|
|
667
|
+
return False
|
|
658
668
|
elif isinstance(color, str):
|
|
659
669
|
return bool(_re.fullmatch(Regex.rgba_str(allow_alpha=allow_alpha), color))
|
|
660
670
|
return False
|
|
@@ -672,16 +682,20 @@ class Color:
|
|
|
672
682
|
0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
|
|
673
683
|
and (0 <= color[3] <= 1 or color[3] is None)
|
|
674
684
|
)
|
|
675
|
-
|
|
685
|
+
elif len(color) == 3:
|
|
676
686
|
return 0 <= color[0] <= 360 and 0 <= color[1] <= 100 and 0 <= color[2] <= 100
|
|
687
|
+
else:
|
|
688
|
+
return False
|
|
677
689
|
elif isinstance(color, dict):
|
|
678
690
|
if allow_alpha and Color.has_alpha(color):
|
|
679
691
|
return (
|
|
680
692
|
0 <= color["h"] <= 360 and 0 <= color["s"] <= 100 and 0 <= color["l"] <= 100
|
|
681
693
|
and (0 <= color["a"] <= 1 or color["a"] is None)
|
|
682
694
|
)
|
|
683
|
-
|
|
695
|
+
elif len(color) == 3:
|
|
684
696
|
return 0 <= color["h"] <= 360 and 0 <= color["s"] <= 100 and 0 <= color["l"] <= 100
|
|
697
|
+
else:
|
|
698
|
+
return False
|
|
685
699
|
elif isinstance(color, str):
|
|
686
700
|
return bool(_re.fullmatch(Regex.hsla_str(allow_alpha=allow_alpha), color))
|
|
687
701
|
except Exception:
|
|
@@ -691,9 +705,9 @@ class Color:
|
|
|
691
705
|
def is_valid_hexa(color: str | int, allow_alpha: bool = True, get_prefix: bool = False) -> bool | tuple[bool, str]:
|
|
692
706
|
try:
|
|
693
707
|
if isinstance(color, hexa):
|
|
694
|
-
return (True, "#")
|
|
708
|
+
return (True, "#") if get_prefix else True
|
|
695
709
|
elif isinstance(color, int):
|
|
696
|
-
is_valid =
|
|
710
|
+
is_valid = 0x000000 <= color <= (0xFFFFFFFF if allow_alpha else 0xFFFFFF)
|
|
697
711
|
return (is_valid, "0x") if get_prefix else is_valid
|
|
698
712
|
elif isinstance(color, str):
|
|
699
713
|
color, prefix = ((color[1:], "#") if color.startswith("#") else
|
|
@@ -705,7 +719,7 @@ class Color:
|
|
|
705
719
|
|
|
706
720
|
@staticmethod
|
|
707
721
|
def is_valid(color: str | list | tuple | dict, allow_alpha: bool = True) -> bool:
|
|
708
|
-
return (
|
|
722
|
+
return bool(
|
|
709
723
|
Color.is_valid_rgba(color, allow_alpha) or Color.is_valid_hsla(color, allow_alpha)
|
|
710
724
|
or Color.is_valid_hexa(color, allow_alpha)
|
|
711
725
|
)
|
|
@@ -777,7 +791,7 @@ class Color:
|
|
|
777
791
|
--------------------------------------------------------------------------------------------------
|
|
778
792
|
If `only_first` is `True` only the first found color will be returned (not as a list)."""
|
|
779
793
|
if only_first:
|
|
780
|
-
match = _re.search(Regex.
|
|
794
|
+
match = _re.search(Regex.rgba_str(allow_alpha=True), string)
|
|
781
795
|
if not match:
|
|
782
796
|
return None
|
|
783
797
|
m = match.groups()
|
|
@@ -789,7 +803,7 @@ class Color:
|
|
|
789
803
|
_validate=False,
|
|
790
804
|
)
|
|
791
805
|
else:
|
|
792
|
-
matches = _re.findall(Regex.
|
|
806
|
+
matches = _re.findall(Regex.rgba_str(allow_alpha=True), string)
|
|
793
807
|
if not matches:
|
|
794
808
|
return None
|
|
795
809
|
return [
|
|
@@ -865,28 +879,47 @@ class Color:
|
|
|
865
879
|
raise ValueError(f"Invalid HEX integer '0x{hex_str}': expected in range [0x000000, 0xFFFFFF]")
|
|
866
880
|
|
|
867
881
|
@staticmethod
|
|
868
|
-
def luminance(r: int, g: int, b: int, output_type: type = None) -> int | float:
|
|
869
|
-
"""
|
|
870
|
-
|
|
871
|
-
The
|
|
872
|
-
- `int`
|
|
873
|
-
- `float`
|
|
874
|
-
- `None`
|
|
882
|
+
def luminance(r: int, g: int, b: int, output_type: type = None, method: str = "wcag2") -> int | float:
|
|
883
|
+
"""Calculates the relative luminance of a color according to various standards.\n
|
|
884
|
+
----------------------------------------------------------------------------------
|
|
885
|
+
The `output_type` controls the range of the returned luminance value:
|
|
886
|
+
- `int` returns integer in [0, 100]
|
|
887
|
+
- `float` returns float in [0.0, 1.0]
|
|
888
|
+
- `None` returns integer in [0, 255]\n
|
|
889
|
+
The `method` is the luminance calculation method to use:
|
|
890
|
+
- `"wcag2"` WCAG 2.0 standard (default and most accurate for perception)
|
|
891
|
+
- `"wcag3"` Draft WCAG 3.0 standard with improved coefficients
|
|
892
|
+
- `"simple"` Simple arithmetic mean (less accurate)
|
|
893
|
+
- `"bt601"` ITU-R BT.601 standard (older TV standard)"""
|
|
875
894
|
r, g, b = r / 255.0, g / 255.0, b / 255.0
|
|
876
|
-
if
|
|
877
|
-
|
|
895
|
+
if method == "simple":
|
|
896
|
+
luminance = (r + g + b) / 3
|
|
897
|
+
elif method == "bt601":
|
|
898
|
+
luminance = 0.299 * r + 0.587 * g + 0.114 * b
|
|
899
|
+
elif method == "wcag3":
|
|
900
|
+
r = Color._linearize_srgb(r)
|
|
901
|
+
g = Color._linearize_srgb(g)
|
|
902
|
+
b = Color._linearize_srgb(b)
|
|
903
|
+
luminance = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b
|
|
878
904
|
else:
|
|
879
|
-
r = (
|
|
880
|
-
|
|
881
|
-
|
|
905
|
+
r = Color._linearize_srgb(r)
|
|
906
|
+
g = Color._linearize_srgb(g)
|
|
907
|
+
b = Color._linearize_srgb(b)
|
|
908
|
+
luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
909
|
+
if output_type == int:
|
|
910
|
+
return round(luminance * 100)
|
|
911
|
+
elif output_type == float:
|
|
912
|
+
return luminance
|
|
882
913
|
else:
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
914
|
+
return round(luminance * 255)
|
|
915
|
+
|
|
916
|
+
@staticmethod
|
|
917
|
+
def _linearize_srgb(c: float) -> float:
|
|
918
|
+
"""Helper method to linearize sRGB component following the WCAG standard."""
|
|
919
|
+
if c <= 0.03928:
|
|
920
|
+
return c / 12.92
|
|
886
921
|
else:
|
|
887
|
-
|
|
888
|
-
l = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
889
|
-
return round(l * 100) if isinstance(output_type, int) else round(l * 255) if output_type is None else l
|
|
922
|
+
return ((c + 0.055) / 1.055)**2.4
|
|
890
923
|
|
|
891
924
|
@staticmethod
|
|
892
925
|
def text_color_for_on_bg(text_bg_color: rgba | hexa) -> rgba | hexa:
|