xulbux 1.6.0__py3-none-any.whl → 1.6.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 +16 -16
- xulbux/xx_code.py +4 -4
- xulbux/xx_color.py +155 -144
- xulbux/xx_console.py +26 -41
- xulbux/xx_data.py +69 -66
- xulbux/xx_env_path.py +4 -4
- xulbux/xx_file.py +35 -26
- xulbux/xx_format_codes.py +122 -64
- xulbux/xx_json.py +27 -22
- xulbux/xx_path.py +2 -7
- xulbux/xx_regex.py +61 -61
- xulbux/xx_string.py +14 -13
- xulbux/xx_system.py +59 -8
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/METADATA +3 -3
- xulbux-1.6.2.dist-info/RECORD +21 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/WHEEL +1 -1
- xulbux-1.6.0.dist-info/RECORD +0 -21
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/LICENSE +0 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.0.dist-info → xulbux-1.6.2.dist-info}/top_level.txt +0 -0
xulbux/xx_format_codes.py
CHANGED
|
@@ -1,56 +1,74 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Functions to be able to use special (easy) formatting codes directly inside some message (string)
|
|
2
|
+
Functions to be able to use special (easy) formatting codes directly inside some message (string).\n
|
|
3
3
|
These codes, when used within following functions, will change the look of log within the console:
|
|
4
|
-
- `FormatCodes.print()` (
|
|
5
|
-
- `FormatCodes.input()` (
|
|
6
|
-
- `FormatCodes.to_ansi()` (
|
|
4
|
+
- `FormatCodes.print()` (print a special format-codes containing string)
|
|
5
|
+
- `FormatCodes.input()` (input with a special format-codes containing prompt)
|
|
6
|
+
- `FormatCodes.to_ansi()` (transform all special format-codes into ANSI codes in a string)\n
|
|
7
7
|
--------------------------------------------------------------------------------------------------------------------
|
|
8
|
-
How to change the text format and color
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
How to change the text format and color?\n
|
|
9
|
+
Example string with formatting codes:
|
|
10
|
+
```string
|
|
11
|
+
[bold]This is bold text, [#F08]which is pink now [black|BG:#FF0088] and now it changed`
|
|
12
|
+
to black with a pink background. [_]And this is the boring text, where everything is reset.
|
|
13
|
+
```
|
|
14
|
+
⇾ Instead of writing the formats all separate `[x][y][z]` you can join them like this `[x|y|z]`\n
|
|
13
15
|
--------------------------------------------------------------------------------------------------------------------
|
|
14
|
-
You can also automatically reset a certain format, behind text like shown in the following example
|
|
15
|
-
|
|
16
|
-
This
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
You can also automatically reset a certain format, behind text like shown in the following example:
|
|
17
|
+
```string
|
|
18
|
+
This is normal text [b](which is bold now) but now it was automatically reset to normal.
|
|
19
|
+
```
|
|
20
|
+
This will only reset formats, that have a reset listed below. Colors and BG-colors won't be reset.\n
|
|
21
|
+
This is what will happen, if you use it with a color-format:
|
|
22
|
+
```string
|
|
23
|
+
[cyan]This is cyan text [b](which is bold now.) Now it's not bold any more but still cyan.
|
|
24
|
+
```
|
|
25
|
+
If you want to ignore the `()` brackets you can put a `\\` or `/` between:
|
|
26
|
+
```string
|
|
27
|
+
[cyan]This is cyan text [b]/(which is bold now.) And now it is still bold and cyan.
|
|
28
|
+
```
|
|
29
|
+
⇾ To see these examples in action, you can put them into the `FormatCodes.print()` function.\n
|
|
22
30
|
--------------------------------------------------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
- HEX colors: `[#F08]` or `[#FF0088]` (
|
|
31
|
+
All possible formatting codes:
|
|
32
|
+
- HEX colors: `[#F08]` or `[#FF0088]` (with or without leading #)
|
|
25
33
|
- RGB colors: `[rgb(255, 0, 136)]`
|
|
26
|
-
- bright colors: `[bright:#F08]`
|
|
27
34
|
- background colors: `[BG:#F08]`
|
|
28
35
|
- standard cmd colors:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
- `[black]`
|
|
37
|
+
- `[red]`
|
|
38
|
+
- `[green]`
|
|
39
|
+
- `[yellow]`
|
|
40
|
+
- `[blue]`
|
|
41
|
+
- `[magenta]`
|
|
42
|
+
- `[cyan]`
|
|
43
|
+
- `[white]`
|
|
37
44
|
- bright cmd colors: `[bright:black]` or `[br:black]`, `[bright:red]` or `[br:red]`, ...
|
|
38
45
|
- background cmd colors: `[BG:black]`, `[BG:red]`, ...
|
|
39
|
-
- bright background cmd colors: `[BG:bright:black]` or `[BG:br:black]`, `[BG:bright:red]` or `[BG:br:red]`,
|
|
40
|
-
|
|
46
|
+
- bright background cmd colors: `[BG:bright:black]` or `[BG:br:black]`, `[BG:bright:red]` or `[BG:br:red]`, ...\n
|
|
47
|
+
⇾ The order of `BG:` and `bright:` or `br:` does not matter.
|
|
41
48
|
- text formats:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- specific reset:
|
|
51
|
-
-
|
|
49
|
+
- `[bold]` or `[b]`
|
|
50
|
+
- `[dim]`
|
|
51
|
+
- `[italic]` or `[i]`
|
|
52
|
+
- `[underline]` or `[u]`
|
|
53
|
+
- `[inverse]`, `[invert]` or `[in]`
|
|
54
|
+
- `[hidden]`, `[hide]` or `[h]`
|
|
55
|
+
- `[strikethrough]` or `[s]`
|
|
56
|
+
- `[double-underline]` or `[du]`
|
|
57
|
+
- specific reset:
|
|
58
|
+
- `[_bold]` or `[_b]`
|
|
59
|
+
- `[_dim]`
|
|
60
|
+
- `[_italic]` or `[_i]`
|
|
61
|
+
- `[_underline]` or `[_u]`
|
|
62
|
+
- `[_inverse]`, `[_invert]` or `[_in]`
|
|
63
|
+
- `[_hidden]`, `[_hide]` or `[_h]`
|
|
64
|
+
- `[_strikethrough]` or `[_s]`
|
|
65
|
+
- `[_double-underline]` or `[_du]`
|
|
66
|
+
- `[_color]` or `[_c]`
|
|
67
|
+
- `[_background]` or `[_bg]`
|
|
68
|
+
- total reset:
|
|
69
|
+
- `[_]`
|
|
52
70
|
--------------------------------------------------------------------------------------------------------------------
|
|
53
|
-
|
|
71
|
+
Additional formats when a `default_color` is set:
|
|
54
72
|
- `[*]` will reset everything, just like `[_]`, but the text-color will remain in `default_color`
|
|
55
73
|
- `[*color]` will reset the text-color, just like `[_color]`, but then also make it `default_color`
|
|
56
74
|
- `[default]` will just color the text in `default_color`,
|
|
@@ -78,20 +96,32 @@ import regex as _rx
|
|
|
78
96
|
import sys as _sys
|
|
79
97
|
import re as _re
|
|
80
98
|
|
|
81
|
-
|
|
99
|
+
PREFIX = {
|
|
100
|
+
"BG": {"background", "bg"},
|
|
101
|
+
"BR": {"bright", "br"},
|
|
102
|
+
}
|
|
103
|
+
PREFIXES = {val for values in PREFIX.values() for val in values}
|
|
104
|
+
PREFIX_RX = {
|
|
105
|
+
"BG": rf"(?:{'|'.join(PREFIX['BG'])})\s*:",
|
|
106
|
+
"BR": rf"(?:{'|'.join(PREFIX['BR'])})\s*:",
|
|
107
|
+
}
|
|
82
108
|
COMPILED = { # PRECOMPILE REGULAR EXPRESSIONS
|
|
83
109
|
"*": _re.compile(r"\[\s*([^]_]*?)\s*\*\s*([^]_]*?)\]"),
|
|
84
110
|
"*color": _re.compile(r"\[\s*([^]_]*?)\s*\*color\s*([^]_]*?)\]"),
|
|
85
111
|
"format": _rx.compile(
|
|
86
112
|
Regex.brackets("[", "]", is_group=True) + r"(?:\s*([/\\]?)\s*" + Regex.brackets("(", ")", is_group=True) + r")?"
|
|
87
113
|
),
|
|
88
|
-
"bg?_default": _re.compile(r"(?i)((?:BG
|
|
89
|
-
"bg_default": _re.compile(r"(?i)BG\s
|
|
114
|
+
"bg?_default": _re.compile(r"(?i)((?:" + PREFIX_RX["BG"] + r")?)\s*default"),
|
|
115
|
+
"bg_default": _re.compile(r"(?i)" + PREFIX_RX["BG"] + r"\s*default"),
|
|
90
116
|
"modifier": _re.compile(
|
|
91
|
-
|
|
117
|
+
r"(?i)((?:BG\s*:)?)\s*("
|
|
118
|
+
+ "|".join([f"{_re.escape(m)}+" for m in ANSI.modifier["lighten"] + ANSI.modifier["darken"]])
|
|
119
|
+
+ r")$"
|
|
92
120
|
),
|
|
93
|
-
"rgb": _re.compile(
|
|
94
|
-
|
|
121
|
+
"rgb": _re.compile(
|
|
122
|
+
r"(?i)^\s*(" + PREFIX_RX["BG"] + r")?\s*(?:rgb|rgba)?\s*\(?\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)?\s*$"
|
|
123
|
+
),
|
|
124
|
+
"hex": _re.compile(r"(?i)^\s*(" + PREFIX_RX["BG"] + r")?\s*(?:#|0x)?([0-9A-F]{6}|[0-9A-F]{3})\s*$"),
|
|
95
125
|
}
|
|
96
126
|
|
|
97
127
|
|
|
@@ -106,6 +136,10 @@ class FormatCodes:
|
|
|
106
136
|
end: str = "\n",
|
|
107
137
|
flush: bool = True,
|
|
108
138
|
) -> None:
|
|
139
|
+
"""Print a string that can be formatted using special formatting codes.\n
|
|
140
|
+
--------------------------------------------------------------------------
|
|
141
|
+
For exact information about how to use special formatting codes, see the
|
|
142
|
+
`xx_format_codes` module documentation."""
|
|
109
143
|
FormatCodes.__config_console()
|
|
110
144
|
_sys.stdout.write(FormatCodes.to_ansi(sep.join(map(str, values)) + end, default_color, brightness_steps))
|
|
111
145
|
if flush:
|
|
@@ -116,16 +150,29 @@ class FormatCodes:
|
|
|
116
150
|
prompt: object = "",
|
|
117
151
|
default_color: hexa | rgba = None,
|
|
118
152
|
brightness_steps: int = 20,
|
|
153
|
+
reset_ansi: bool = False,
|
|
119
154
|
) -> str:
|
|
155
|
+
"""An input, which's prompt can be formatted using special formatting codes.\n
|
|
156
|
+
-------------------------------------------------------------------------------
|
|
157
|
+
If `reset_ansi` is true, all ANSI formatting will be reset, after the user has
|
|
158
|
+
confirmed the input and the program continues.\n
|
|
159
|
+
-------------------------------------------------------------------------------
|
|
160
|
+
For exact information about how to use special formatting codes, see the
|
|
161
|
+
`xx_format_codes` module documentation."""
|
|
120
162
|
FormatCodes.__config_console()
|
|
121
|
-
|
|
163
|
+
user_input = input(FormatCodes.to_ansi(prompt, default_color, brightness_steps))
|
|
164
|
+
if reset_ansi:
|
|
165
|
+
_sys.stdout.write("\x1b[0m")
|
|
166
|
+
return user_input
|
|
122
167
|
|
|
123
168
|
@staticmethod
|
|
124
169
|
def to_ansi(
|
|
125
170
|
string: str, default_color: hexa | rgba = None, brightness_steps: int = 20, _default_start: bool = True
|
|
126
171
|
) -> str:
|
|
127
|
-
|
|
128
|
-
|
|
172
|
+
"""Convert the special formatting codes inside a string to printable ANSI codes.\n
|
|
173
|
+
-----------------------------------------------------------------------------------
|
|
174
|
+
For exact information about how to use special formatting codes, see the
|
|
175
|
+
`xx_format_codes` module documentation."""
|
|
129
176
|
if Color.is_valid_rgba(default_color, False):
|
|
130
177
|
use_default = True
|
|
131
178
|
elif Color.is_valid_hexa(default_color, False):
|
|
@@ -158,10 +205,9 @@ class FormatCodes:
|
|
|
158
205
|
reset_keys = []
|
|
159
206
|
for k in format_keys:
|
|
160
207
|
k_lower = k.lower()
|
|
161
|
-
|
|
162
|
-
k_set
|
|
163
|
-
|
|
164
|
-
if k_set & color_pref:
|
|
208
|
+
k_set = set(k_lower.split(":"))
|
|
209
|
+
if PREFIX["BG"] & k_set and len(k_set) <= 3:
|
|
210
|
+
if k_set & PREFIX["BR"]:
|
|
165
211
|
for i in range(len(k)):
|
|
166
212
|
if is_valid_color(k[i:]):
|
|
167
213
|
reset_keys.extend(["_bg", "_color"])
|
|
@@ -173,7 +219,7 @@ class FormatCodes:
|
|
|
173
219
|
break
|
|
174
220
|
elif is_valid_color(k) or any(
|
|
175
221
|
k_lower.startswith(pref_colon := f"{prefix}:") and is_valid_color(k[len(pref_colon) :])
|
|
176
|
-
for prefix in
|
|
222
|
+
for prefix in PREFIX["BR"]
|
|
177
223
|
):
|
|
178
224
|
reset_keys.append("_color")
|
|
179
225
|
else:
|
|
@@ -201,8 +247,8 @@ class FormatCodes:
|
|
|
201
247
|
+ ("" if escaped else "".join(ansi_resets))
|
|
202
248
|
)
|
|
203
249
|
|
|
204
|
-
|
|
205
|
-
return (FormatCodes.__get_default_ansi(default_color) if _default_start else "") +
|
|
250
|
+
string = "\n".join(COMPILED["format"].sub(replace_keys, line) for line in string.split("\n"))
|
|
251
|
+
return (FormatCodes.__get_default_ansi(default_color) if _default_start else "") + string if use_default else string
|
|
206
252
|
|
|
207
253
|
@staticmethod
|
|
208
254
|
def escape_ansi(ansi_string: str, escaped_char: str = ANSI.char_esc) -> str:
|
|
@@ -212,6 +258,7 @@ class FormatCodes:
|
|
|
212
258
|
@staticmethod
|
|
213
259
|
@lru_cache(maxsize=64)
|
|
214
260
|
def __config_console() -> None:
|
|
261
|
+
"""Configure the console to be able to interpret ANSI formatting."""
|
|
215
262
|
_sys.stdout.flush()
|
|
216
263
|
kernel32 = _ctypes.windll.kernel32
|
|
217
264
|
h = kernel32.GetStdHandle(-11)
|
|
@@ -226,6 +273,7 @@ class FormatCodes:
|
|
|
226
273
|
brightness_steps: int = None,
|
|
227
274
|
_modifiers: tuple[str, str] = (ANSI.modifier["lighten"], ANSI.modifier["darken"]),
|
|
228
275
|
) -> str | None:
|
|
276
|
+
"""Get the `default_color` and lighter/darker versions of it in ANSI format."""
|
|
229
277
|
if not brightness_steps or (format_key and COMPILED["bg?_default"].search(format_key)):
|
|
230
278
|
return (ANSI.seq_bg_color if format_key and COMPILED["bg_default"].search(format_key) else ANSI.seq_color).format(
|
|
231
279
|
*default_color[:3]
|
|
@@ -252,15 +300,11 @@ class FormatCodes:
|
|
|
252
300
|
|
|
253
301
|
@staticmethod
|
|
254
302
|
def __get_replacement(format_key: str, default_color: rgba = None, brightness_steps: int = 20) -> str:
|
|
255
|
-
"""Gives you the corresponding ANSI code for the given format key
|
|
256
|
-
If `default_color` is not `None`, the text color will be `default_color` if all formats
|
|
303
|
+
"""Gives you the corresponding ANSI code for the given format key.
|
|
304
|
+
If `default_color` is not `None`, the text color will be `default_color` if all formats
|
|
257
305
|
are reset or you can get lighter or darker version of `default_color` (also as BG)"""
|
|
258
306
|
use_default = default_color and Color.is_valid_rgba(default_color, False)
|
|
259
|
-
_format_key, format_key = format_key, ( # NORMALIZE
|
|
260
|
-
"bg:" if "bg" in (parts := format_key.replace(" ", "").lower().split(":")) else ""
|
|
261
|
-
) + ("bright:" if any(x in parts for x in ("bright", "br")) else "") + ":".join(
|
|
262
|
-
p for p in parts if p not in ("bg", "bright", "br")
|
|
263
|
-
)
|
|
307
|
+
_format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
|
|
264
308
|
if use_default:
|
|
265
309
|
if new_default_color := FormatCodes.__get_default_ansi(default_color, format_key, brightness_steps):
|
|
266
310
|
return new_default_color
|
|
@@ -295,3 +339,17 @@ class FormatCodes:
|
|
|
295
339
|
except Exception:
|
|
296
340
|
pass
|
|
297
341
|
return _format_key
|
|
342
|
+
|
|
343
|
+
@staticmethod
|
|
344
|
+
@lru_cache(maxsize=64)
|
|
345
|
+
def __normalize_key(format_key: str) -> str:
|
|
346
|
+
"""Normalizes the given format key."""
|
|
347
|
+
k_parts = format_key.replace(" ", "").lower().split(":")
|
|
348
|
+
prefix_str = "".join(
|
|
349
|
+
f"{prefix_key.lower()}:"
|
|
350
|
+
for prefix_key, prefix_values in PREFIX.items()
|
|
351
|
+
if any(k_part in prefix_values for k_part in k_parts)
|
|
352
|
+
)
|
|
353
|
+
return prefix_str + ":".join(
|
|
354
|
+
part for part in k_parts if part not in {val for values in PREFIX.values() for val in values}
|
|
355
|
+
)
|
xulbux/xx_json.py
CHANGED
|
@@ -16,13 +16,15 @@ class Json:
|
|
|
16
16
|
) -> dict | tuple[dict, dict]:
|
|
17
17
|
"""Read JSON files, ignoring comments.\n
|
|
18
18
|
-------------------------------------------------------------------------
|
|
19
|
-
If only `comment_start` is found at the beginning of an item
|
|
20
|
-
the whole item is counted as a comment and therefore ignored
|
|
21
|
-
If `comment_start` and `comment_end` are found inside an item
|
|
22
|
-
the the section from `comment_start` to `comment_end` is ignored
|
|
23
|
-
If `return_original` is set to `True`, the original JSON is returned
|
|
19
|
+
If only `comment_start` is found at the beginning of an item,
|
|
20
|
+
the whole item is counted as a comment and therefore ignored.
|
|
21
|
+
If `comment_start` and `comment_end` are found inside an item,
|
|
22
|
+
the the section from `comment_start` to `comment_end` is ignored.
|
|
23
|
+
If `return_original` is set to `True`, the original JSON is returned
|
|
24
24
|
additionally. (returns: `[processed_json, original_json]`)"""
|
|
25
|
-
|
|
25
|
+
if not json_file.endswith(".json"):
|
|
26
|
+
json_file += ".json"
|
|
27
|
+
file_path = File.extend_or_make_path(json_file, prefer_base_dir=True)
|
|
26
28
|
with open(file_path, "r") as f:
|
|
27
29
|
content = f.read()
|
|
28
30
|
try:
|
|
@@ -42,7 +44,9 @@ class Json:
|
|
|
42
44
|
compactness: int = 1,
|
|
43
45
|
force: bool = False,
|
|
44
46
|
) -> str:
|
|
45
|
-
|
|
47
|
+
if not new_file.endswith(".json"):
|
|
48
|
+
new_file += ".json"
|
|
49
|
+
file_path = File.extend_or_make_path(new_file, prefer_base_dir=True)
|
|
46
50
|
if _os.path.exists(file_path) and not force:
|
|
47
51
|
with open(file_path, "r", encoding="utf-8") as existing_f:
|
|
48
52
|
existing_content = _json.load(existing_f)
|
|
@@ -63,27 +67,28 @@ class Json:
|
|
|
63
67
|
sep: tuple[str, str] = ("->", "::"),
|
|
64
68
|
) -> None:
|
|
65
69
|
"""Function to easily update single/multiple values inside JSON files.\n
|
|
66
|
-
|
|
70
|
+
------------------------------------------------------------------------------------------------------
|
|
67
71
|
The param `json_file` is the path to the JSON file or just the name of the JSON file to be updated.\n
|
|
68
|
-
|
|
69
|
-
The param `update_values` is a sort of path (or a list of paths) to the value/s to be updated, with
|
|
70
|
-
the new value at the end of the path
|
|
72
|
+
------------------------------------------------------------------------------------------------------
|
|
73
|
+
The param `update_values` is a sort of path (or a list of paths) to the value/s to be updated, with
|
|
74
|
+
the new value at the end of the path.\n
|
|
71
75
|
In this example:
|
|
72
|
-
|
|
76
|
+
```python
|
|
77
|
+
{
|
|
73
78
|
'healthy': {
|
|
74
79
|
'fruit': ['apples', 'bananas', 'oranges'],
|
|
75
80
|
'vegetables': ['carrots', 'broccoli', 'celery']
|
|
76
81
|
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"""
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
... if you want to change the value of `'apples'` to `'strawberries'`, `update_values` would be
|
|
85
|
+
`healthy->fruit->apples::strawberries` or if you don't know that the value to update is `apples` you
|
|
86
|
+
can also use the position of the value, so `healthy->fruit->0::strawberries`.\n
|
|
87
|
+
⇾ If the path from `update_values` doesn't exist, it will be created.\n
|
|
88
|
+
------------------------------------------------------------------------------------------------------
|
|
89
|
+
If only `comment_start` is found at the beginning of an item, the whole item is counted as a comment
|
|
90
|
+
and therefore ignored. If `comment_start` and `comment_end` are found inside an item, the the section
|
|
91
|
+
from `comment_start` to `comment_end` is ignored."""
|
|
87
92
|
if isinstance(update_values, str):
|
|
88
93
|
update_values = [update_values]
|
|
89
94
|
valid_entries = [
|
xulbux/xx_path.py
CHANGED
|
@@ -8,7 +8,7 @@ import os as _os
|
|
|
8
8
|
class Path:
|
|
9
9
|
|
|
10
10
|
@staticmethod
|
|
11
|
-
def get(cwd: bool = False, base_dir: bool = False) -> str | list:
|
|
11
|
+
def get(cwd: bool = False, base_dir: bool = False) -> str | list[str]:
|
|
12
12
|
paths = []
|
|
13
13
|
if cwd:
|
|
14
14
|
paths.append(_os.getcwd())
|
|
@@ -29,12 +29,7 @@ class Path:
|
|
|
29
29
|
return paths[0] if len(paths) == 1 else paths
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
32
|
-
def extend(
|
|
33
|
-
path: str,
|
|
34
|
-
search_in: str | list[str] = None,
|
|
35
|
-
raise_error: bool = False,
|
|
36
|
-
correct_path: bool = False,
|
|
37
|
-
) -> str:
|
|
32
|
+
def extend(path: str, search_in: str | list[str] = None, raise_error: bool = False, correct_path: bool = False) -> str:
|
|
38
33
|
if path in (None, ""):
|
|
39
34
|
return path
|
|
40
35
|
|
xulbux/xx_regex.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Really long regex code presets
|
|
3
|
-
`quotes` match everything inside quotes
|
|
4
|
-
`brackets` match everything inside brackets
|
|
5
|
-
`outside_strings` match the pattern but not inside strings
|
|
6
|
-
`all_except` match everything except a certain pattern
|
|
7
|
-
`func_call` match a function call
|
|
8
|
-
`rgba_str` match an RGBA color
|
|
2
|
+
Really long regex code presets:
|
|
3
|
+
`quotes` match everything inside quotes
|
|
4
|
+
`brackets` match everything inside brackets
|
|
5
|
+
`outside_strings` match the pattern but not inside strings
|
|
6
|
+
`all_except` match everything except a certain pattern
|
|
7
|
+
`func_call` match a function call
|
|
8
|
+
`rgba_str` match an RGBA color
|
|
9
9
|
`hsla_str` match a HSLA color
|
|
10
10
|
"""
|
|
11
11
|
|
|
@@ -16,20 +16,20 @@ class Regex:
|
|
|
16
16
|
|
|
17
17
|
@staticmethod
|
|
18
18
|
def quotes() -> str:
|
|
19
|
-
"""Matches everything inside quotes. (
|
|
19
|
+
"""Matches everything inside quotes. (strings)\n
|
|
20
20
|
------------------------------------------------------------------------------------
|
|
21
|
-
Will create two named groups
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
Will create two named groups:
|
|
22
|
+
- `quote` the quote type (single or double)
|
|
23
|
+
- `string` everything inside the found quote pair\n
|
|
24
24
|
------------------------------------------------------------------------------------
|
|
25
|
-
|
|
25
|
+
Attention: Requires non standard library `regex` not standard library `re`!"""
|
|
26
26
|
return r'(?P<quote>[\'"])(?P<string>(?:\\.|(?!\g<quote>).)*?)\g<quote>'
|
|
27
27
|
|
|
28
28
|
@staticmethod
|
|
29
29
|
def brackets(bracket1: str = "(", bracket2: str = ")", is_group: bool = False) -> str:
|
|
30
30
|
"""Matches everything inside brackets, including other nested brackets.\n
|
|
31
31
|
------------------------------------------------------------------------------------
|
|
32
|
-
|
|
32
|
+
Attention: Requires non standard library `regex` not standard library `re`!"""
|
|
33
33
|
g, b1, b2 = (
|
|
34
34
|
"" if is_group else "?:",
|
|
35
35
|
_re.escape(bracket1) if len(bracket1) == 1 else bracket1,
|
|
@@ -50,40 +50,40 @@ class Regex:
|
|
|
50
50
|
) -> str:
|
|
51
51
|
"""Matches everything except `disallowed_pattern`, unless the `disallowed_pattern` is found inside a string (`'...'` or `"..."`).\n
|
|
52
52
|
------------------------------------------------------------------------------------------------------------------------------------
|
|
53
|
-
The `ignore_pattern` is just always ignored. For example if `disallowed_pattern` is `>` and `ignore_pattern` is `->`, the
|
|
54
|
-
-arrows will be allowed, even though they have `>` in them. If `is_group` is `True`, you will be able to reference the matched
|
|
55
|
-
content as a group (e.g.
|
|
53
|
+
The `ignore_pattern` is just always ignored. For example if `disallowed_pattern` is `>` and `ignore_pattern` is `->`, the `->`
|
|
54
|
+
-arrows will be allowed, even though they have `>` in them. If `is_group` is `True`, you will be able to reference the matched
|
|
55
|
+
content as a group (e.g. `match.group(int)` or `r'\\int'`)."""
|
|
56
56
|
return rf'({"" if is_group else "?:"}(?:(?!{ignore_pattern}).)*(?:(?!{Regex.outside_strings(disallowed_pattern)}).)*)'
|
|
57
57
|
|
|
58
58
|
@staticmethod
|
|
59
59
|
def func_call(func_name: str = None) -> str:
|
|
60
|
-
"""Match a function call
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
"""Match a function call
|
|
61
|
+
- `1` function name
|
|
62
|
+
- `2` the function's arguments\n
|
|
63
63
|
If no `func_name` is given, it will match any function call.\n
|
|
64
64
|
------------------------------------------------------------------------------------
|
|
65
|
-
|
|
65
|
+
Attention: Requires non standard library `regex` not standard library `re`!"""
|
|
66
66
|
return r"(?<=\b)(" + (func_name if func_name else r"[\w_]+") + r")\s*" + Regex.brackets("(", ")", is_group=True)
|
|
67
67
|
|
|
68
68
|
@staticmethod
|
|
69
69
|
def rgba_str(fix_sep: str = ",", allow_alpha: bool = True) -> str:
|
|
70
70
|
"""Matches an RGBA color inside a string.\n
|
|
71
|
-
|
|
72
|
-
The RGBA color can be in the formats (for `fix_sep = ','`)
|
|
73
|
-
`rgba(r, g, b)
|
|
74
|
-
`rgba(r, g, b, a)` (if `allow_alpha=True`)
|
|
75
|
-
`(r, g, b)
|
|
76
|
-
`(r, g, b, a)` (if `allow_alpha=True`)
|
|
77
|
-
`r, g, b
|
|
78
|
-
`r, g, b, a` (if `allow_alpha=True`)\n
|
|
79
|
-
|
|
80
|
-
### Valid ranges
|
|
81
|
-
`r` 0-255 (amount: red)
|
|
82
|
-
`g` 0-255 (amount: green)
|
|
83
|
-
`b` 0-255 (amount: blue)
|
|
84
|
-
`a` 0-1 (float: opacity)\n
|
|
85
|
-
|
|
86
|
-
If the `fix_sep` is set to nothing, any char that is not a letter or number
|
|
71
|
+
----------------------------------------------------------------------------
|
|
72
|
+
The RGBA color can be in the formats (for `fix_sep = ','`):
|
|
73
|
+
- `rgba(r, g, b)`
|
|
74
|
+
- `rgba(r, g, b, a)` (if `allow_alpha=True`)
|
|
75
|
+
- `(r, g, b)`
|
|
76
|
+
- `(r, g, b, a)` (if `allow_alpha=True`)
|
|
77
|
+
- `r, g, b`
|
|
78
|
+
- `r, g, b, a` (if `allow_alpha=True`)\n
|
|
79
|
+
----------------------------------------------------------------------------
|
|
80
|
+
### Valid ranges:
|
|
81
|
+
- `r` 0-255 (amount: red)
|
|
82
|
+
- `g` 0-255 (amount: green)
|
|
83
|
+
- `b` 0-255 (amount: blue)
|
|
84
|
+
- `a` 0-1 (float: opacity)\n
|
|
85
|
+
----------------------------------------------------------------------------
|
|
86
|
+
If the `fix_sep` is set to nothing, any char that is not a letter or number
|
|
87
87
|
can be used to separate the RGBA values, including just a space."""
|
|
88
88
|
if fix_sep in (None, ""):
|
|
89
89
|
fix_sep = r"[^0-9A-Z]"
|
|
@@ -104,22 +104,22 @@ class Regex:
|
|
|
104
104
|
@staticmethod
|
|
105
105
|
def hsla_str(fix_sep: str = ",", allow_alpha: bool = True) -> str:
|
|
106
106
|
"""Matches a HSLA color inside a string.\n
|
|
107
|
-
|
|
108
|
-
The HSLA color can be in the formats (for `fix_sep = ','`)
|
|
109
|
-
`hsla(h, s, l)
|
|
110
|
-
`hsla(h, s, l, a)` (if `allow_alpha=True`)
|
|
111
|
-
`(h, s, l)
|
|
112
|
-
`(h, s, l, a)` (if `allow_alpha=True`)
|
|
113
|
-
`h, s, l
|
|
114
|
-
`h, s, l, a` (if `allow_alpha=True`)\n
|
|
115
|
-
|
|
116
|
-
### Valid ranges
|
|
117
|
-
`h` 0-360 (degrees: hue)
|
|
118
|
-
`s` 0-100 (percentage: saturation)
|
|
119
|
-
`l` 0-100 (percentage: lightness)
|
|
120
|
-
`a` 0-1 (float: opacity)\n
|
|
121
|
-
|
|
122
|
-
If the `fix_sep` is set to nothing, any char that is not a letter or number
|
|
107
|
+
----------------------------------------------------------------------------
|
|
108
|
+
The HSLA color can be in the formats (for `fix_sep = ','`):
|
|
109
|
+
- `hsla(h, s, l)`
|
|
110
|
+
- `hsla(h, s, l, a)` (if `allow_alpha=True`)
|
|
111
|
+
- `(h, s, l)`
|
|
112
|
+
- `(h, s, l, a)` (if `allow_alpha=True`)
|
|
113
|
+
- `h, s, l`
|
|
114
|
+
- `h, s, l, a` (if `allow_alpha=True`)\n
|
|
115
|
+
----------------------------------------------------------------------------
|
|
116
|
+
### Valid ranges:
|
|
117
|
+
- `h` 0-360 (degrees: hue)
|
|
118
|
+
- `s` 0-100 (percentage: saturation)
|
|
119
|
+
- `l` 0-100 (percentage: lightness)
|
|
120
|
+
- `a` 0-1 (float: opacity)\n
|
|
121
|
+
----------------------------------------------------------------------------
|
|
122
|
+
If the `fix_sep` is set to nothing, any char that is not a letter or number
|
|
123
123
|
can be used to separate the HSLA values, including just a space."""
|
|
124
124
|
if fix_sep in (None, ""):
|
|
125
125
|
fix_sep = r"[^0-9A-Z]"
|
|
@@ -140,15 +140,15 @@ class Regex:
|
|
|
140
140
|
@staticmethod
|
|
141
141
|
def hexa_str(allow_alpha: bool = True) -> str:
|
|
142
142
|
"""Matches a HEXA color inside a string.\n
|
|
143
|
-
|
|
144
|
-
The HEXA color can be in the formats (prefix `#`, `0x` or no prefix)
|
|
145
|
-
`RGB
|
|
146
|
-
`RGBA` (if `allow_alpha=True`)
|
|
147
|
-
`RRGGBB
|
|
148
|
-
`RRGGBBAA` (if `allow_alpha=True`)\n
|
|
149
|
-
|
|
150
|
-
### Valid ranges
|
|
151
|
-
each channel from 0-9 and A-F (
|
|
143
|
+
----------------------------------------------------------------------
|
|
144
|
+
The HEXA color can be in the formats (prefix `#`, `0x` or no prefix):
|
|
145
|
+
- `RGB`
|
|
146
|
+
- `RGBA` (if `allow_alpha=True`)
|
|
147
|
+
- `RRGGBB`
|
|
148
|
+
- `RRGGBBAA` (if `allow_alpha=True`)\n
|
|
149
|
+
----------------------------------------------------------------------
|
|
150
|
+
### Valid ranges:
|
|
151
|
+
each channel from 0-9 and A-F (case insensitive)"""
|
|
152
152
|
return (
|
|
153
153
|
r"(?i)^(?:#|0x)?[0-9A-F]{8}|[0-9A-F]{6}|[0-9A-F]{4}|[0-9A-F]{3}$"
|
|
154
154
|
if allow_alpha
|
xulbux/xx_string.py
CHANGED
|
@@ -56,7 +56,7 @@ class String:
|
|
|
56
56
|
|
|
57
57
|
@staticmethod
|
|
58
58
|
def normalize_spaces(string: str, tab_spaces: int = 4) -> str:
|
|
59
|
-
"""Replaces all special space characters with normal spaces
|
|
59
|
+
"""Replaces all special space characters with normal spaces.
|
|
60
60
|
Also replaces tab characters with `tab_spaces` spaces."""
|
|
61
61
|
return (
|
|
62
62
|
string.replace("\t", " " * tab_spaces)
|
|
@@ -76,10 +76,10 @@ class String:
|
|
|
76
76
|
@staticmethod
|
|
77
77
|
def escape(string: str, str_quotes: str = '"') -> str:
|
|
78
78
|
"""Escapes the special characters and quotes inside a string.\n
|
|
79
|
-
|
|
80
|
-
`str_quotes` can be either `"` or `'` and should match the quotes
|
|
81
|
-
the string will be put inside of. So if your string will be `"string"
|
|
82
|
-
you should pass `"` to the parameter `str_quotes
|
|
79
|
+
---------------------------------------------------------------------------
|
|
80
|
+
`str_quotes` can be either `"` or `'` and should match the quotes,
|
|
81
|
+
the string will be put inside of. So if your string will be `"string"`,
|
|
82
|
+
you should pass `"` to the parameter `str_quotes`.
|
|
83
83
|
That way, if the string includes the same quotes, they will be escaped."""
|
|
84
84
|
string = (
|
|
85
85
|
string.replace("\\", r"\\")
|
|
@@ -98,14 +98,15 @@ class String:
|
|
|
98
98
|
|
|
99
99
|
@staticmethod
|
|
100
100
|
def is_empty(string: str, spaces_are_empty: bool = False):
|
|
101
|
-
"""Returns `True` if the string is empty and `False` otherwise
|
|
101
|
+
"""Returns `True` if the string is empty and `False` otherwise.\n
|
|
102
|
+
-------------------------------------------------------------------------------------------
|
|
102
103
|
If `spaces_are_empty` is true, it will also return `True` if the string is only spaces."""
|
|
103
104
|
return (string in (None, "")) or (spaces_are_empty and isinstance(string, str) and not string.strip())
|
|
104
105
|
|
|
105
106
|
@staticmethod
|
|
106
107
|
def single_char_repeats(string: str, char: str) -> int | bool:
|
|
107
|
-
"""If the string consists of only the same `char`, it returns the number of times it is present
|
|
108
|
-
If the string doesn't consist of only the same character, it returns `False`."""
|
|
108
|
+
"""- If the string consists of only the same `char`, it returns the number of times it is present.
|
|
109
|
+
- If the string doesn't consist of only the same character, it returns `False`."""
|
|
109
110
|
if len(string) == len(char) * string.count(char):
|
|
110
111
|
return string.count(char)
|
|
111
112
|
else:
|
|
@@ -113,7 +114,7 @@ class String:
|
|
|
113
114
|
|
|
114
115
|
@staticmethod
|
|
115
116
|
def decompose(case_string: str, seps: str = "-_", lower_all: bool = True) -> list[str]:
|
|
116
|
-
"""Will decompose the string (
|
|
117
|
+
"""Will decompose the string (any type of casing, also mixed) into parts."""
|
|
117
118
|
return [
|
|
118
119
|
(part.lower() if lower_all else part)
|
|
119
120
|
for part in _re.split(rf"(?<=[a-z])(?=[A-Z])|[{_re.escape(seps)}]", case_string)
|
|
@@ -121,7 +122,7 @@ class String:
|
|
|
121
122
|
|
|
122
123
|
@staticmethod
|
|
123
124
|
def to_camel_case(string: str, upper: bool = True) -> str:
|
|
124
|
-
"""Will convert the string of any type of casing to UpperCamelCase or lowerCamelCase if `upper` is false."""
|
|
125
|
+
"""Will convert the string of any type of casing to `UpperCamelCase` or `lowerCamelCase` if `upper` is false."""
|
|
125
126
|
parts = String.decompose(string)
|
|
126
127
|
return ("" if upper else parts[0].lower()) + "".join(part.capitalize() for part in (parts if upper else parts[1:]))
|
|
127
128
|
|
|
@@ -146,9 +147,9 @@ class String:
|
|
|
146
147
|
@staticmethod
|
|
147
148
|
def remove_consecutive_empty_lines(string: str, max_consecutive: int = 0) -> str:
|
|
148
149
|
"""Will remove consecutive empty lines from the string.\n
|
|
149
|
-
|
|
150
|
-
If `max_consecutive` is `0`, it will remove all consecutive empty lines
|
|
151
|
-
If `max_consecutive` is bigger than `0`, it will only allow `max_consecutive` consecutive
|
|
150
|
+
--------------------------------------------------------------------------------------------
|
|
151
|
+
- If `max_consecutive` is `0`, it will remove all consecutive empty lines.
|
|
152
|
+
- If `max_consecutive` is bigger than `0`, it will only allow `max_consecutive` consecutive
|
|
152
153
|
empty lines and everything above it will be cut down to `max_consecutive` empty lines."""
|
|
153
154
|
return _re.sub(r"(\n\s*){2,}", r"\1" * (max_consecutive + 1), string)
|
|
154
155
|
|