xulbux 1.6.9__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 +2 -2
- xulbux/_cli_.py +20 -27
- xulbux/_consts_.py +1 -0
- xulbux/xx_code.py +1 -1
- xulbux/xx_color.py +144 -101
- xulbux/xx_console.py +168 -64
- xulbux/xx_data.py +22 -21
- xulbux/xx_env_path.py +6 -5
- xulbux/xx_file.py +2 -2
- xulbux/xx_format_codes.py +37 -28
- xulbux/xx_json.py +7 -6
- xulbux/xx_path.py +7 -7
- xulbux/xx_regex.py +15 -10
- xulbux/xx_system.py +5 -5
- {xulbux-1.6.9.dist-info → xulbux-1.7.1.dist-info}/METADATA +36 -34
- xulbux-1.7.1.dist-info/RECORD +21 -0
- {xulbux-1.6.9.dist-info → xulbux-1.7.1.dist-info}/WHEEL +1 -1
- xulbux-1.6.9.dist-info/RECORD +0 -21
- {xulbux-1.6.9.dist-info → xulbux-1.7.1.dist-info}/entry_points.txt +0 -0
- {xulbux-1.6.9.dist-info → xulbux-1.7.1.dist-info}/licenses/LICENSE +0 -0
- {xulbux-1.6.9.dist-info → xulbux-1.7.1.dist-info}/top_level.txt +0 -0
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
|
|
|
@@ -152,10 +153,10 @@ Per default, you can also use `+` and `-` to get lighter and darker `default_col
|
|
|
152
153
|
|
|
153
154
|
from ._consts_ import ANSI
|
|
154
155
|
from .xx_string import String
|
|
155
|
-
from .xx_regex import Regex
|
|
156
|
-
from .xx_color import Color, rgba,
|
|
156
|
+
from .xx_regex import Regex, Match, Pattern
|
|
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)
|
|
@@ -205,7 +206,7 @@ class FormatCodes:
|
|
|
205
206
|
@staticmethod
|
|
206
207
|
def print(
|
|
207
208
|
*values: object,
|
|
208
|
-
default_color:
|
|
209
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
209
210
|
brightness_steps: int = 20,
|
|
210
211
|
sep: str = " ",
|
|
211
212
|
end: str = "\n",
|
|
@@ -223,7 +224,7 @@ class FormatCodes:
|
|
|
223
224
|
@staticmethod
|
|
224
225
|
def input(
|
|
225
226
|
prompt: object = "",
|
|
226
|
-
default_color:
|
|
227
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
227
228
|
brightness_steps: int = 20,
|
|
228
229
|
reset_ansi: bool = False,
|
|
229
230
|
) -> str:
|
|
@@ -243,7 +244,7 @@ class FormatCodes:
|
|
|
243
244
|
@staticmethod
|
|
244
245
|
def to_ansi(
|
|
245
246
|
string: str,
|
|
246
|
-
default_color:
|
|
247
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
247
248
|
brightness_steps: int = 20,
|
|
248
249
|
_default_start: bool = True,
|
|
249
250
|
) -> str:
|
|
@@ -253,20 +254,23 @@ class FormatCodes:
|
|
|
253
254
|
`xx_format_codes` module documentation."""
|
|
254
255
|
if not isinstance(string, str):
|
|
255
256
|
string = str(string)
|
|
256
|
-
if 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 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
|
-
return color in ANSI.color_map or Color.is_valid_rgba(color) or Color.is_valid_hexa(color)
|
|
271
|
+
return bool((color in ANSI.color_map) or Color.is_valid_rgba(color) or Color.is_valid_hexa(color))
|
|
268
272
|
|
|
269
|
-
def replace_keys(match:
|
|
273
|
+
def replace_keys(match: Match) -> str:
|
|
270
274
|
_formats = formats = match.group(1)
|
|
271
275
|
auto_reset_escaped = match.group(2)
|
|
272
276
|
auto_reset_txt = match.group(3)
|
|
@@ -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,7 +306,7 @@ 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 = [
|
|
@@ -325,7 +329,8 @@ class FormatCodes:
|
|
|
325
329
|
)
|
|
326
330
|
|
|
327
331
|
string = "\n".join(_COMPILED["formatting"].sub(replace_keys, line) for line in string.split("\n"))
|
|
328
|
-
return ((FormatCodes.__get_default_ansi(default_color)
|
|
332
|
+
return (((FormatCodes.__get_default_ansi(default_color.values()) or "") if _default_start else "")
|
|
333
|
+
+ string) if default_color is not None else string
|
|
329
334
|
|
|
330
335
|
@staticmethod
|
|
331
336
|
def escape_ansi(ansi_string: str) -> str:
|
|
@@ -346,7 +351,7 @@ class FormatCodes:
|
|
|
346
351
|
if get_removals:
|
|
347
352
|
removals = []
|
|
348
353
|
|
|
349
|
-
def replacement(match:
|
|
354
|
+
def replacement(match: Match) -> str:
|
|
350
355
|
start_pos = match.start() - sum(len(removed) for _, removed in removals)
|
|
351
356
|
if removals and removals[-1][0] == start_pos:
|
|
352
357
|
start_pos = removals[-1][0]
|
|
@@ -364,6 +369,7 @@ class FormatCodes:
|
|
|
364
369
|
@staticmethod
|
|
365
370
|
def remove_formatting(
|
|
366
371
|
string: str,
|
|
372
|
+
default_color: Optional[Rgba | Hexa] = None,
|
|
367
373
|
get_removals: bool = False,
|
|
368
374
|
_ignore_linebreaks: bool = False,
|
|
369
375
|
) -> str | tuple[str, tuple[tuple[int, str], ...]]:
|
|
@@ -373,7 +379,7 @@ class FormatCodes:
|
|
|
373
379
|
Each tuple contains the position of the removed formatting code and the removed formatting code.\n
|
|
374
380
|
If `_ignore_linebreaks` is true, linebreaks will be ignored for the removal positions."""
|
|
375
381
|
return FormatCodes.remove_ansi(
|
|
376
|
-
FormatCodes.to_ansi(string),
|
|
382
|
+
FormatCodes.to_ansi(string, default_color=default_color),
|
|
377
383
|
get_removals=get_removals,
|
|
378
384
|
_ignore_linebreaks=_ignore_linebreaks,
|
|
379
385
|
)
|
|
@@ -393,9 +399,9 @@ class FormatCodes:
|
|
|
393
399
|
|
|
394
400
|
@staticmethod
|
|
395
401
|
def __get_default_ansi(
|
|
396
|
-
default_color: tuple,
|
|
397
|
-
format_key: str = None,
|
|
398
|
-
brightness_steps: int = None,
|
|
402
|
+
default_color: tuple[int, int, int],
|
|
403
|
+
format_key: Optional[str] = None,
|
|
404
|
+
brightness_steps: Optional[int] = None,
|
|
399
405
|
_modifiers: tuple[str, str] = (ANSI.default_color_modifiers["lighten"], ANSI.default_color_modifiers["darken"]),
|
|
400
406
|
) -> Optional[str]:
|
|
401
407
|
"""Get the `default_color` and lighter/darker versions of it as ANSI code."""
|
|
@@ -403,8 +409,9 @@ class FormatCodes:
|
|
|
403
409
|
return (ANSI.seq_bg_color if format_key and _COMPILED["bg_default"].search(format_key) else ANSI.seq_color).format(
|
|
404
410
|
*default_color[:3]
|
|
405
411
|
)
|
|
406
|
-
if not (format_key in _modifiers[0] or format_key in _modifiers[1]):
|
|
412
|
+
if format_key is None or not (format_key in _modifiers[0] or format_key in _modifiers[1]):
|
|
407
413
|
return None
|
|
414
|
+
assert format_key is not None
|
|
408
415
|
match = _COMPILED["modifier"].match(format_key)
|
|
409
416
|
if not match:
|
|
410
417
|
return None
|
|
@@ -415,23 +422,25 @@ class FormatCodes:
|
|
|
415
422
|
if adjust and adjust > 0:
|
|
416
423
|
modifiers = mod
|
|
417
424
|
break
|
|
425
|
+
new_rgb = default_color
|
|
418
426
|
if adjust == 0:
|
|
419
427
|
return None
|
|
420
428
|
elif modifiers in _modifiers[0]:
|
|
421
|
-
new_rgb = Color.adjust_lightness(default_color, (brightness_steps / 100) * adjust)
|
|
429
|
+
new_rgb = tuple(Color.adjust_lightness(default_color, (brightness_steps / 100) * adjust))
|
|
422
430
|
elif modifiers in _modifiers[1]:
|
|
423
|
-
new_rgb = Color.adjust_lightness(default_color, -(brightness_steps / 100) * adjust)
|
|
431
|
+
new_rgb = tuple(Color.adjust_lightness(default_color, -(brightness_steps / 100) * adjust))
|
|
424
432
|
return (ANSI.seq_bg_color if is_bg else ANSI.seq_color).format(*new_rgb[:3])
|
|
425
433
|
|
|
426
434
|
@staticmethod
|
|
427
|
-
def __get_replacement(format_key: str, default_color: rgba
|
|
435
|
+
def __get_replacement(format_key: str, default_color: Optional[rgba], brightness_steps: int = 20) -> str:
|
|
428
436
|
"""Gives you the corresponding ANSI code for the given format key.
|
|
429
437
|
If `default_color` is not `None`, the text color will be `default_color` if all formats
|
|
430
438
|
are reset or you can get lighter or darker version of `default_color` (also as BG)"""
|
|
431
439
|
use_default = default_color and Color.is_valid_rgba(default_color, False)
|
|
440
|
+
_default_color = tuple(Color.to_rgba(default_color)) if default_color is not None else tuple()
|
|
432
441
|
_format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
|
|
433
442
|
if use_default:
|
|
434
|
-
if new_default_color := FormatCodes.__get_default_ansi(
|
|
443
|
+
if new_default_color := FormatCodes.__get_default_ansi(_default_color, format_key, brightness_steps):
|
|
435
444
|
return new_default_color
|
|
436
445
|
for map_key in ANSI.codes_map:
|
|
437
446
|
if (isinstance(map_key, tuple) and format_key in map_key) or format_key == map_key:
|
|
@@ -440,8 +449,8 @@ class FormatCodes:
|
|
|
440
449
|
v for k, v in ANSI.codes_map.items() if format_key == k or (isinstance(k, tuple) and format_key in k)
|
|
441
450
|
), None)
|
|
442
451
|
)
|
|
443
|
-
rgb_match =
|
|
444
|
-
hex_match =
|
|
452
|
+
rgb_match = _COMPILED["rgb"].match(format_key)
|
|
453
|
+
hex_match = _COMPILED["hex"].match(format_key)
|
|
445
454
|
try:
|
|
446
455
|
if rgb_match:
|
|
447
456
|
is_bg = rgb_match.group(1)
|
xulbux/xx_json.py
CHANGED
|
@@ -26,13 +26,15 @@ class Json:
|
|
|
26
26
|
if not json_file.endswith(".json"):
|
|
27
27
|
json_file += ".json"
|
|
28
28
|
file_path = Path.extend_or_make(json_file, prefer_script_dir=True)
|
|
29
|
+
if file_path is None:
|
|
30
|
+
raise FileNotFoundError(f"Could not find JSON file: {json_file}")
|
|
29
31
|
with open(file_path, "r") as f:
|
|
30
32
|
content = f.read()
|
|
31
33
|
try:
|
|
32
34
|
data = _json.loads(content)
|
|
33
35
|
except _json.JSONDecodeError as e:
|
|
34
36
|
raise ValueError(f"Error parsing JSON in '{file_path}': {str(e)}")
|
|
35
|
-
processed_data = Data.remove_comments(data, comment_start, comment_end)
|
|
37
|
+
processed_data = dict(Data.remove_comments(data, comment_start, comment_end))
|
|
36
38
|
if not processed_data:
|
|
37
39
|
raise ValueError(f"The JSON file '{file_path}' is empty or contains only comments.")
|
|
38
40
|
return (processed_data, data) if return_original else processed_data
|
|
@@ -135,24 +137,23 @@ class Json:
|
|
|
135
137
|
raise TypeError(f"Cannot navigate through {type(current).__name__}")
|
|
136
138
|
return data_obj
|
|
137
139
|
|
|
140
|
+
update = {}
|
|
138
141
|
for value_path, new_value in update_values.items():
|
|
139
142
|
try:
|
|
140
143
|
path_id = Data.get_path_id(
|
|
141
144
|
data=processed_data,
|
|
142
145
|
value_paths=value_path,
|
|
143
146
|
path_sep=path_sep,
|
|
144
|
-
ignore_not_found=True,
|
|
145
147
|
)
|
|
146
148
|
if path_id is not None:
|
|
147
|
-
if 'update' not in locals():
|
|
148
|
-
update = {}
|
|
149
149
|
update[path_id] = new_value
|
|
150
150
|
else:
|
|
151
|
+
keys = value_path.split(path_sep)
|
|
151
152
|
keys = value_path.split(path_sep)
|
|
152
153
|
data = create_nested_path(data, keys, new_value)
|
|
153
154
|
except Exception:
|
|
154
155
|
keys = value_path.split(path_sep)
|
|
155
156
|
data = create_nested_path(data, keys, new_value)
|
|
156
|
-
if
|
|
157
|
+
if "update" in locals() and update:
|
|
157
158
|
data = Data.set_value_by_path_id(data, update)
|
|
158
|
-
Json.create(json_file=json_file, data=data, force=True)
|
|
159
|
+
Json.create(json_file=json_file, data=dict(data), force=True)
|
xulbux/xx_path.py
CHANGED
|
@@ -23,9 +23,9 @@ class _ScriptDir:
|
|
|
23
23
|
base_path = _os.path.dirname(_sys.executable)
|
|
24
24
|
else:
|
|
25
25
|
main_module = _sys.modules["__main__"]
|
|
26
|
-
if hasattr(main_module, "__file__"):
|
|
26
|
+
if hasattr(main_module, "__file__") and main_module.__file__ is not None:
|
|
27
27
|
base_path = _os.path.dirname(_os.path.abspath(main_module.__file__))
|
|
28
|
-
elif (hasattr(main_module, "__spec__") and main_module.__spec__ and
|
|
28
|
+
elif (hasattr(main_module, "__spec__") and main_module.__spec__ and main_module.__spec__.origin is not None):
|
|
29
29
|
base_path = _os.path.dirname(_os.path.abspath(main_module.__spec__.origin))
|
|
30
30
|
else:
|
|
31
31
|
raise RuntimeError("Can only get base directory if accessed from a file.")
|
|
@@ -34,15 +34,15 @@ class _ScriptDir:
|
|
|
34
34
|
|
|
35
35
|
class Path:
|
|
36
36
|
|
|
37
|
-
cwd: str = _Cwd()
|
|
37
|
+
cwd: str = _Cwd() # type: ignore[assignment]
|
|
38
38
|
"""The path to the current working directory."""
|
|
39
|
-
script_dir: str = _ScriptDir()
|
|
39
|
+
script_dir: str = _ScriptDir() # type: ignore[assignment]
|
|
40
40
|
"""The path to the directory of the current script."""
|
|
41
41
|
|
|
42
42
|
@staticmethod
|
|
43
43
|
def extend(
|
|
44
44
|
rel_path: str,
|
|
45
|
-
search_in: str | list[str] = None,
|
|
45
|
+
search_in: Optional[str | list[str]] = None,
|
|
46
46
|
raise_error: bool = False,
|
|
47
47
|
use_closest_match: bool = False,
|
|
48
48
|
) -> Optional[str]:
|
|
@@ -120,7 +120,7 @@ class Path:
|
|
|
120
120
|
@staticmethod
|
|
121
121
|
def extend_or_make(
|
|
122
122
|
rel_path: str,
|
|
123
|
-
search_in: str | list[str] = None,
|
|
123
|
+
search_in: Optional[str | list[str]] = None,
|
|
124
124
|
prefer_script_dir: bool = True,
|
|
125
125
|
use_closest_match: bool = False,
|
|
126
126
|
) -> str:
|
|
@@ -136,7 +136,7 @@ class Path:
|
|
|
136
136
|
If `use_closest_match` is true, it is possible to have typos in the `search_in` path/s
|
|
137
137
|
and it will still find the file if it is under one of those paths."""
|
|
138
138
|
try:
|
|
139
|
-
return Path.extend(rel_path, search_in, raise_error=True, use_closest_match=use_closest_match)
|
|
139
|
+
return str(Path.extend(rel_path, search_in, raise_error=True, use_closest_match=use_closest_match))
|
|
140
140
|
except PathNotFoundError:
|
|
141
141
|
normalized_rel_path = _os.path.normpath(rel_path)
|
|
142
142
|
base = Path.script_dir if prefer_script_dir else _os.getcwd()
|
xulbux/xx_regex.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
from typing import TypeAlias, Optional
|
|
1
2
|
import regex as _rx
|
|
2
3
|
import re as _re
|
|
3
4
|
|
|
4
5
|
|
|
6
|
+
Pattern: TypeAlias = _re.Pattern[str] | _rx.Pattern[str]
|
|
7
|
+
Match: TypeAlias = _re.Match[str] | _rx.Match[str]
|
|
8
|
+
|
|
9
|
+
|
|
5
10
|
class Regex:
|
|
6
11
|
|
|
7
12
|
@staticmethod
|
|
8
13
|
def quotes() -> str:
|
|
9
|
-
"""Matches
|
|
14
|
+
"""Matches pairs of quotes. (strings)\n
|
|
10
15
|
--------------------------------------------------------------------------------
|
|
11
16
|
Will create two named groups:
|
|
12
17
|
- `quote` the quote type (single or double)
|
|
13
18
|
- `string` everything inside the found quote pair\n
|
|
14
|
-
|
|
15
|
-
Attention: Requires non
|
|
19
|
+
---------------------------------------------------------------------------------
|
|
20
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
16
21
|
return r'(?P<quote>[\'"])(?P<string>(?:\\.|(?!\g<quote>).)*?)\g<quote>'
|
|
17
22
|
|
|
18
23
|
@staticmethod
|
|
@@ -23,16 +28,16 @@ class Regex:
|
|
|
23
28
|
strip_spaces: bool = True,
|
|
24
29
|
ignore_in_strings: bool = True,
|
|
25
30
|
) -> str:
|
|
26
|
-
"""Matches everything inside brackets, including other nested brackets.\n
|
|
27
|
-
|
|
31
|
+
"""Matches everything inside pairs of brackets, including other nested brackets.\n
|
|
32
|
+
-----------------------------------------------------------------------------------
|
|
28
33
|
If `is_group` is true, you will be able to reference the matched content as a
|
|
29
34
|
group (e.g. `match.group(…)` or `r'\\…'`).
|
|
30
35
|
If `strip_spaces` is true, it will ignore spaces around the content inside the
|
|
31
36
|
brackets.
|
|
32
37
|
If `ignore_in_strings` is true and a bracket is inside a string (e.g. `'...'`
|
|
33
38
|
or `"..."`), it will not be counted as the matching closing bracket.\n
|
|
34
|
-
|
|
35
|
-
Attention: Requires non
|
|
39
|
+
-----------------------------------------------------------------------------------
|
|
40
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
36
41
|
g, b1, b2, s1, s2 = (
|
|
37
42
|
"" if is_group else "?:",
|
|
38
43
|
_rx.escape(bracket1) if len(bracket1) == 1 else bracket1,
|
|
@@ -63,13 +68,13 @@ class Regex:
|
|
|
63
68
|
return rf'({"" if is_group else "?:"}(?:(?!{ignore_pattern}).)*(?:(?!{Regex.outside_strings(disallowed_pattern)}).)*)'
|
|
64
69
|
|
|
65
70
|
@staticmethod
|
|
66
|
-
def func_call(func_name: str = None) -> str:
|
|
71
|
+
def func_call(func_name: Optional[str] = None) -> str:
|
|
67
72
|
"""Match a function call, and get back two groups:
|
|
68
73
|
1. function name
|
|
69
74
|
2. the function's arguments\n
|
|
70
75
|
If no `func_name` is given, it will match any function call.\n
|
|
71
|
-
|
|
72
|
-
Attention: Requires non
|
|
76
|
+
---------------------------------------------------------------------------------
|
|
77
|
+
Attention: Requires non-standard library `regex`, not standard library `re`!"""
|
|
73
78
|
return (
|
|
74
79
|
r"(?<=\b)(" + (r"[\w_]+" if func_name is None else func_name) + r")\s*" + Regex.brackets("(", ")", is_group=True)
|
|
75
80
|
)
|
xulbux/xx_system.py
CHANGED
|
@@ -14,7 +14,7 @@ class _IsElevated:
|
|
|
14
14
|
if _os.name == "nt":
|
|
15
15
|
return _ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
16
16
|
elif _os.name == "posix":
|
|
17
|
-
return _os.geteuid() == 0
|
|
17
|
+
return _os.geteuid() == 0 # type: ignore[attr-defined]
|
|
18
18
|
except Exception:
|
|
19
19
|
pass
|
|
20
20
|
return False
|
|
@@ -22,7 +22,7 @@ class _IsElevated:
|
|
|
22
22
|
|
|
23
23
|
class System:
|
|
24
24
|
|
|
25
|
-
is_elevated: bool = _IsElevated()
|
|
25
|
+
is_elevated: bool = _IsElevated() # type: ignore[assignment]
|
|
26
26
|
"""Is `True` if the current process has
|
|
27
27
|
elevated privileges and `False` otherwise."""
|
|
28
28
|
|
|
@@ -54,7 +54,7 @@ class System:
|
|
|
54
54
|
if len(processes) > 2: # EXCLUDING THE PYTHON PROCESS AND PS
|
|
55
55
|
raise RuntimeError("Processes are still running. Use the parameter `force=True` to restart anyway.")
|
|
56
56
|
if prompt:
|
|
57
|
-
_subprocess.Popen(["notify-send", "System Restart", prompt])
|
|
57
|
+
_subprocess.Popen(["notify-send", "System Restart", str(prompt)])
|
|
58
58
|
_time.sleep(wait)
|
|
59
59
|
try:
|
|
60
60
|
_subprocess.run(["sudo", "shutdown", "-r", "now"])
|
|
@@ -95,7 +95,7 @@ class System:
|
|
|
95
95
|
return missing
|
|
96
96
|
|
|
97
97
|
@staticmethod
|
|
98
|
-
def elevate(win_title: Optional[str] = None, args:
|
|
98
|
+
def elevate(win_title: Optional[str] = None, args: list = []) -> bool:
|
|
99
99
|
"""Attempts to start a new process with elevated privileges.\n
|
|
100
100
|
---------------------------------------------------------------------------------
|
|
101
101
|
The param `win_title` is window the title of the elevated process.
|
|
@@ -106,7 +106,7 @@ class System:
|
|
|
106
106
|
---------------------------------------------------------------------------------
|
|
107
107
|
Returns `True` if the current process already has elevated privileges and raises
|
|
108
108
|
a `PermissionError` if the user denied the elevation or the elevation failed."""
|
|
109
|
-
if System.is_elevated
|
|
109
|
+
if System.is_elevated:
|
|
110
110
|
return True
|
|
111
111
|
if _os.name == "nt": # WINDOWS
|
|
112
112
|
if win_title:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xulbux
|
|
3
|
-
Version: 1.
|
|
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/)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
xulbux/__init__.py,sha256=4qceHbARrYX-ui-kl5bufH3BlEvMwJpsXG1TQsbNHgA,815
|
|
2
|
+
xulbux/_cli_.py,sha256=J4vfJHLJEYxCZzA_VJUB46w2WGShfdYFoetsLG5PfKo,3428
|
|
3
|
+
xulbux/_consts_.py,sha256=AuYTTmqrP2lawyVGlPLUaP1syxOoPA-ejJGH7WlwFzk,6314
|
|
4
|
+
xulbux/xx_code.py,sha256=w9yO-GPMeaE-xDi-L3VtpPpWpu5jOwagfMsG93aXANE,6106
|
|
5
|
+
xulbux/xx_color.py,sha256=Thj7fFTc8x-VmYyULI3HQW0uCk9dIihwrGUwuXGn83s,49744
|
|
6
|
+
xulbux/xx_console.py,sha256=mruEcbehVceXuDhIMV_WoFec8MxXf2orvlNQWkdI-zE,34367
|
|
7
|
+
xulbux/xx_data.py,sha256=nEfVwK6-ILaL3K-bLezKpG1G7117CY5ZgC3BGwANrUE,30886
|
|
8
|
+
xulbux/xx_env_path.py,sha256=x56mKK4lSvU5yMCAs8k0RVIqXWUJcpcHYz5HoZ_RklM,4160
|
|
9
|
+
xulbux/xx_file.py,sha256=KerXOvKS93zIoAt36YTYuZboSmxVFVf2WcOrDcdwXfE,2627
|
|
10
|
+
xulbux/xx_format_codes.py,sha256=NihbRkPjDSWJCL2itZBlvhbO0bWx8Mm2P-LpSeaG3po,23732
|
|
11
|
+
xulbux/xx_json.py,sha256=V7vdfpvSe9wpktR_c8zG_Meix7x9IRmn66k5nB3HUyo,7457
|
|
12
|
+
xulbux/xx_path.py,sha256=lLAEVZrW0TAwCewlONFVQcQ_8tVn9LTJZVOZpeGvE5s,7673
|
|
13
|
+
xulbux/xx_regex.py,sha256=_BtMHRDNcD9zF4SL87dQuUVZcYGfZx9H5YNSDiEtzm8,8059
|
|
14
|
+
xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
|
|
15
|
+
xulbux/xx_system.py,sha256=Tsx4wgztUg46KloqcGeiFkarDoM3EgJLXw3XNxgHBmU,6460
|
|
16
|
+
xulbux-1.7.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,,
|
xulbux-1.6.9.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
xulbux/__init__.py,sha256=mpAtWO0eumk7FZt6QS1Wvp7KWBsZd8oseSxUPSpAVJk,815
|
|
2
|
-
xulbux/_cli_.py,sha256=SaBlkIx73nfU6r2TbjQVWxOM-R0xTBqOAKtgi2FF-KA,3470
|
|
3
|
-
xulbux/_consts_.py,sha256=b85O5sePS18z7CJgrVw0V7v88PIG9qnQ7G2bJL71odk,6287
|
|
4
|
-
xulbux/xx_code.py,sha256=laA8osWgIW-QSv6P6Am_c6NocOPf8ZSm20EaVfgOC58,6100
|
|
5
|
-
xulbux/xx_color.py,sha256=NdNh-J89PXPhVcdgKBXThbRTnq1UpBg3yn4aG0fmRAE,47602
|
|
6
|
-
xulbux/xx_console.py,sha256=MJOE3giA6wZW0-VjJmBZaHqnUhOA6vRF41UdkAPIxgk,28019
|
|
7
|
-
xulbux/xx_data.py,sha256=zp-DjMJ_VnC-BQQlqdzdgwhnSRzs0MV356AbIjeGgP4,30696
|
|
8
|
-
xulbux/xx_env_path.py,sha256=A54TObZZwDvNZwv0iwHzEbNiCoEvz16OId-gMiUTHdo,4086
|
|
9
|
-
xulbux/xx_file.py,sha256=Efd7-1FFsXYGd_cH95FwI8Mg6PaA7H11ArBpdBhyVhE,2597
|
|
10
|
-
xulbux/xx_format_codes.py,sha256=QXb7ik_JyZJ6agtWykTerkb6NAPhOtOFh7nMpr-bpWo,22912
|
|
11
|
-
xulbux/xx_json.py,sha256=-Wzlg8pUIsLlYJKrlxeoXoPprPn8lGJ2Uqc36WYyk6U,7390
|
|
12
|
-
xulbux/xx_path.py,sha256=ZCnRJyIO5nigaKJjxNjfEz_4Z_mhBS0ELyJaLU7Lid0,7561
|
|
13
|
-
xulbux/xx_regex.py,sha256=oJ7V2ccQNYbnavvCEIyYGVM8001_pjMV1BRu3NGmMJw,7884
|
|
14
|
-
xulbux/xx_string.py,sha256=QaTo0TQ9m_2USNgQNaVw5ivQt-A1E-e5x8OpIB3xIlY,5561
|
|
15
|
-
xulbux/xx_system.py,sha256=vHuNzxG6fakd4pJ0esJNfGglUtRbqpGJhPODVJqwcV0,6411
|
|
16
|
-
xulbux-1.6.9.dist-info/licenses/LICENSE,sha256=6NflEcvzFEe8_JFVNCPVwZBwBhlLLd4vqQi8WiX_Xk4,1084
|
|
17
|
-
xulbux-1.6.9.dist-info/METADATA,sha256=1wGLeAPcJPPDMfhB_JsLCM7mphazUEqyVE4HIfoUbHI,9222
|
|
18
|
-
xulbux-1.6.9.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
|
|
19
|
-
xulbux-1.6.9.dist-info/entry_points.txt,sha256=a3womfLIMZKnOFiyy-xnVb4g2qkZsHR5FbKKkljcGns,94
|
|
20
|
-
xulbux-1.6.9.dist-info/top_level.txt,sha256=FkK4EZajwfP36fnlrPaR98OrEvZpvdEOdW1T5zTj6og,7
|
|
21
|
-
xulbux-1.6.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|