xulbux 1.6.1__py3-none-any.whl → 1.6.3__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/xx_format_codes.py CHANGED
@@ -1,39 +1,78 @@
1
1
  """
2
- Functions to be able to use special (easy) formatting codes directly inside some message (string).\n
3
- These codes, when used within following functions, will change the look of log within the console:
2
+ Methods to transform formatting codes to ANSI and use them for pretty console output:
4
3
  - `FormatCodes.print()` (print a special format-codes containing string)
5
4
  - `FormatCodes.input()` (input with a special format-codes containing prompt)
6
5
  - `FormatCodes.to_ansi()` (transform all special format-codes into ANSI codes in a string)\n
7
- --------------------------------------------------------------------------------------------------------------------
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.
6
+ ------------------------------------------------------------------------------------------------------------------------------------
7
+ ### The Easy Formatting
8
+
9
+ First, let's take a look at a small example of what a highly styled print string with formatting could look like using this module:
10
+ ```regex
11
+ This here is just unformatted text. [b|u|br:blue](Next we have text that is bright blue + bold + underlined.)\\n
12
+ [#000|bg:#F67](Then there's also black text with a red background.) And finally the ([i](boring)) plain text again.
13
13
  ```
14
- ⇾ Instead of writing the formats all separate `[…][…][…]` you can join them like this `[…|…|…]`\n
15
- --------------------------------------------------------------------------------------------------------------------
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.
14
+
15
+ How all of this exactly works is explained in the sections below. 🠫
16
+
17
+ ------------------------------------------------------------------------------------------------------------------------------------
18
+ #### Formatting Codes and Keys
19
+
20
+ In this module, you can apply styles and colors using simple formatting codes.
21
+ These formatting codes consist of one or multiple different formatting keys in between square brackets.
22
+
23
+ If a formatting code is placed in a print-string, the formatting of that code will be applied to everything behind it until its
24
+ formatting is reset. If applying multiple styles and colors in the same place, instead of writing the formatting keys all into
25
+ separate brackets (e.g. `[x][y][z]`), they can also be put in a single pair of brackets, separated by pipes (e.g. `[x|y|z]`).
26
+
27
+ A list of all possible formatting keys can be found under all possible formatting keys.
28
+
29
+ ------------------------------------------------------------------------------------------------------------------------------------
30
+ #### Auto Resetting Formatting Codes
31
+
32
+ Certain formatting can automatically be reset, behind a certain amount of text, just like shown in the following example:
33
+ ```regex
34
+ This is plain text, [br:blue](which is bright blue now.) Now it was automatically reset to plain again.
19
35
  ```
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.
36
+
37
+ This will only reset formatting codes, that have a specific reset listed below.
38
+ That means if you use it where another formatting is already applied, that formatting is still there after the automatic reset:
39
+ ```regex
40
+ [cyan]This is cyan text, [dim](which is dimmed now.) Now it's not dimmed any more but still cyan.
24
41
  ```
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.
42
+
43
+ If you want to ignore the auto-reset functionality of `()` brackets, you can put a `\\` or `/` between them and
44
+ the formatting code:
45
+ ```regex
46
+ [cyan]This is cyan text, [u]/(which is underlined now.) And now it is still underlined and cyan.
28
47
  ```
29
- ⇾ To see these examples in action, you can put them into the `FormatCodes.print()` function.\n
30
- --------------------------------------------------------------------------------------------------------------------
31
- All possible formatting codes:
32
- - HEX colors: `[#F08]` or `[#FF0088]` (with or without leading #)
33
- - RGB colors: `[rgb(255, 0, 136)]`
34
- - bright colors: `[bright:#F08]`
35
- - background colors: `[BG:#F08]`
36
- - standard cmd colors:
48
+
49
+ ------------------------------------------------------------------------------------------------------------------------------------
50
+ #### All possible Formatting Keys
51
+
52
+ - RGB colors:
53
+ Change the text color directly with an RGB color inside the square brackets. (With or without `rgb()` brackets doesn't matter.)
54
+ Examples:
55
+ - `[rgb(115, 117, 255)]`
56
+ - `[(255, 0, 136)]`
57
+ - `[255, 0, 136]`
58
+ - HEX colors:
59
+ Change the text color directly with a HEX color inside the square brackets. (If `RGB` or `RRGGBB` formatting code is used,
60
+ and if there's a `#` or `0x` prefix, doesn't matter.)
61
+ Examples:
62
+ - `[#7788FF]`
63
+ - `[7788FF]`
64
+ - `[#78F]`
65
+ - `[78F]`
66
+ - background RGB / HEX colors:
67
+ Change the background color directly with an RGB or HEX color inside the square brackets, using the `background:` `BG:` prefix.
68
+ (Same RGB / HEX formatting code rules as for text color.)
69
+ Examples:
70
+ - `[background:rgb(115, 117, 255)]`
71
+ - `[BG:(255, 0, 136)]`
72
+ - `[background:#7788FF]`
73
+ - `[BG:#78F]`
74
+ - standard console colors:
75
+ Change the text color to one of the standard console colors by just writing the color name in the square brackets.
37
76
  - `[black]`
38
77
  - `[red]`
39
78
  - `[green]`
@@ -42,28 +81,64 @@ All possible formatting codes:
42
81
  - `[magenta]`
43
82
  - `[cyan]`
44
83
  - `[white]`
45
- - bright cmd colors: `[bright:black]` or `[br:black]`, `[bright:red]` or `[br:red]`, ...
46
- - background cmd colors: `[BG:black]`, `[BG:red]`, ...
47
- - bright background cmd colors: `[BG:bright:black]` or `[BG:br:black]`, `[BG:bright:red]` or `[BG:br:red]`, ...\n
48
- ⇾ The order of `BG:` and `bright:` or `br:` does not matter.
49
- - text formats:
50
- - `[bold]` or `[b]`
84
+ - bright console colors:
85
+ Use the prefix `bright:` `BR:` to use the bright variant of the standard console color.
86
+ Examples:
87
+ - `[bright:black]` `[BR:black]`
88
+ - `[bright:red]` `[BR:red]`
89
+ - ...
90
+ - Background console colors:
91
+ Use the prefix `background:` `BG:` to set the background to a standard console color. (Not all consoles support bright
92
+ standard colors.)
93
+ Examples:
94
+ - `[background:black]` `[BG:black]`
95
+ - `[background:red]` `[BG:red]`
96
+ - ...
97
+ - Bright background console colors:
98
+ Combine the prefixes `background:` / `BG:` and `bright:` / `BR:` to set the background to a bright console color.
99
+ (The order of the prefixes doesn't matter.)
100
+ Examples:
101
+ - `[background:bright:black]` `[BG:BR:black]`
102
+ - `[background:bright:red]` `[BG:BR:red]`
103
+ - ...
104
+ - Text styles:
105
+ Use the built-in text formatting to change the style of the text. There are long and short forms for each formatting code.
106
+ (Not all consoles support all text styles.)
107
+ - `[bold]` `[b]`
51
108
  - `[dim]`
52
- - `[italic]` or `[i]`
53
- - `[underline]` or `[u]`
54
- - `[inverse]`, `[invert]` or `[in]`
55
- - `[hidden]`, `[hide]` or `[h]`
56
- - `[strikethrough]` or `[s]`
57
- - `[double-underline]` or `[du]`
58
- - specific reset: `[_bold]` or `[_b]`, `[_dim]`, ... or `[_color]` or `[_c]`, `[_background]` or `[_bg]`
59
- - total reset: `[_]` (only if no `default_color` is set, otherwise see )
60
- --------------------------------------------------------------------------------------------------------------------
61
- Special formatting when param `default_color` is set to a color:
62
- - `[*]` will reset everything, just like `[_]`, but the text-color will remain in `default_color`
63
- - `[*color]` will reset the text-color, just like `[_color]`, but then also make it `default_color`
64
- - `[default]` will just color the text in `default_color`,
65
- - `[BG:default]` will color the background in `default_color`\n
66
- Unlike the standard cmd colors, the default color can be changed by using the following modifiers:
109
+ - `[italic]` `[i]`
110
+ - `[underline]` `[u]`
111
+ - `[inverse]` `[invert]` `[in]`
112
+ - `[hidden]` `[hide]` `[h]`
113
+ - `[strikethrough]` `[s]`
114
+ - `[double-underline]` `[du]`
115
+ - Specific reset:
116
+ Use these reset codes to remove a specific style, color or background. Again, there are long and
117
+ short forms for each reset code.
118
+ - `[_bold]` `[_b]`
119
+ - `[_dim]`
120
+ - `[_italic]` `[_i]`
121
+ - `[_underline]` `[_u]`
122
+ - `[_inverse]` `[_invert]` `[_in]`
123
+ - `[_hidden]` `[_hide]` `[_h]`
124
+ - `[_strikethrough]` `[_s]`
125
+ - `[_double-underline]` `[_du]`
126
+ - `[_color]` `[_c]`
127
+ - `[_background]` `[_bg]`
128
+ - Total reset:
129
+ This will reset all previously applied formatting codes.
130
+ - `[_]`
131
+
132
+ ------------------------------------------------------------------------------------------------------------------------------------
133
+ #### Additional Formatting Codes when a `default_color` is set
134
+
135
+ 1. `[*]` resets everything, just like `[_]`, but the text color will remain in `default_color`
136
+ 2. `[*color]` will reset the text color, just like `[_color]`, but then also make it `default_color`
137
+ 3. `[default]` will just color the text in `default_color`
138
+ 4. `[background:default]` `[BG:default]` will color the background in `default_color`
139
+
140
+ Unlike the standard console colors, the default color can be changed by using the following modifiers:
141
+
67
142
  - `[l]` will lighten the `default_color` text by `brightness_steps`%
68
143
  - `[ll]` will lighten the `default_color` text by `2 × brightness_steps`%
69
144
  - `[lll]` will lighten the `default_color` text by `3 × brightness_steps`%
@@ -71,7 +146,7 @@ Unlike the standard cmd colors, the default color can be changed by using the fo
71
146
  - `[d]` will darken the `default_color` text by `brightness_steps`%
72
147
  - `[dd]` will darken the `default_color` text by `2 × brightness_steps`%
73
148
  - `[ddd]` will darken the `default_color` text by `3 × brightness_steps`%
74
- - ... etc.\n
149
+ - ... etc.
75
150
  Per default, you can also use `+` and `-` to get lighter and darker `default_color` versions.
76
151
  """
77
152
 
@@ -80,26 +155,43 @@ from .xx_string import String
80
155
  from .xx_regex import Regex
81
156
  from .xx_color import *
82
157
 
83
- from functools import lru_cache
84
158
  import ctypes as _ctypes
85
159
  import regex as _rx
86
160
  import sys as _sys
87
161
  import re as _re
88
162
 
163
+ _CONSOLE_ANSI_CONFIGURED = False
89
164
 
90
- COMPILED = { # PRECOMPILE REGULAR EXPRESSIONS
165
+ _PREFIX = {
166
+ "BG": {"background", "bg"},
167
+ "BR": {"bright", "br"},
168
+ }
169
+ _PREFIX_RX = {
170
+ "BG": rf"(?:{'|'.join(_PREFIX['BG'])})\s*:",
171
+ "BR": rf"(?:{'|'.join(_PREFIX['BR'])})\s*:",
172
+ }
173
+ _COMPILED = { # PRECOMPILE REGULAR EXPRESSIONS
91
174
  "*": _re.compile(r"\[\s*([^]_]*?)\s*\*\s*([^]_]*?)\]"),
92
175
  "*color": _re.compile(r"\[\s*([^]_]*?)\s*\*color\s*([^]_]*?)\]"),
93
- "format": _rx.compile(
94
- Regex.brackets("[", "]", is_group=True) + r"(?:\s*([/\\]?)\s*" + Regex.brackets("(", ")", is_group=True) + r")?"
176
+ "formatting": _rx.compile(
177
+ Regex.brackets("[", "]", is_group=True)
178
+ + r"(?:\s*([/\\]?)\s*"
179
+ + Regex.brackets("(", ")", is_group=True, ignore_in_strings=False)
180
+ + r")?"
95
181
  ),
96
- "bg?_default": _re.compile(r"(?i)((?:BG\s*:)?)\s*default"),
97
- "bg_default": _re.compile(r"(?i)BG\s*:\s*default"),
182
+ "bg?_default": _re.compile(r"(?i)((?:" + _PREFIX_RX["BG"] + r")?)\s*default"),
183
+ "bg_default": _re.compile(r"(?i)" + _PREFIX_RX["BG"] + r"\s*default"),
98
184
  "modifier": _re.compile(
99
- rf'(?i)((?:BG\s*:)?)\s*({"|".join([f"{_re.escape(m)}+" for m in ANSI.modifier["lighten"] + ANSI.modifier["darken"]])})$'
185
+ r"(?i)((?:BG\s*:)?)\s*("
186
+ + "|".join(
187
+ [f"{_re.escape(m)}+" for m in ANSI.default_color_modifiers["lighten"] + ANSI.default_color_modifiers["darken"]]
188
+ )
189
+ + r")$"
190
+ ),
191
+ "rgb": _re.compile(
192
+ 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*$"
100
193
  ),
101
- "rgb": _re.compile(r"(?i)^\s*(BG\s*:)?\s*(?:rgb|rgba)?\s*\(?\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)?\s*$"),
102
- "hex": _re.compile(r"(?i)^\s*(BG\s*:)?\s*(?:#|0x)?([0-9A-F]{6}|[0-9A-F]{3})\s*$"),
194
+ "hex": _re.compile(r"(?i)^\s*(" + _PREFIX_RX["BG"] + r")?\s*(?:#|0x)?([0-9A-F]{6}|[0-9A-F]{3})\s*$"),
103
195
  }
104
196
 
105
197
 
@@ -108,12 +200,16 @@ class FormatCodes:
108
200
  @staticmethod
109
201
  def print(
110
202
  *values: object,
111
- default_color: hexa | rgba = None,
203
+ default_color: rgba | hexa = None,
112
204
  brightness_steps: int = 20,
113
205
  sep: str = " ",
114
206
  end: str = "\n",
115
207
  flush: bool = True,
116
208
  ) -> None:
209
+ """A print function, whose print values can be formatted using formatting codes.\n
210
+ -----------------------------------------------------------------------------------
211
+ For exact information about how to use special formatting codes, see the
212
+ `xx_format_codes` module documentation."""
117
213
  FormatCodes.__config_console()
118
214
  _sys.stdout.write(FormatCodes.to_ansi(sep.join(map(str, values)) + end, default_color, brightness_steps))
119
215
  if flush:
@@ -122,18 +218,34 @@ class FormatCodes:
122
218
  @staticmethod
123
219
  def input(
124
220
  prompt: object = "",
125
- default_color: hexa | rgba = None,
221
+ default_color: rgba | hexa = None,
126
222
  brightness_steps: int = 20,
223
+ reset_ansi: bool = False,
127
224
  ) -> str:
225
+ """An input, whose prompt can be formatted using formatting codes.\n
226
+ -------------------------------------------------------------------------
227
+ If `reset_ansi` is true, all ANSI formatting will be reset, after the
228
+ user confirms the input and the program continues to run.\n
229
+ -------------------------------------------------------------------------
230
+ For exact information about how to use special formatting codes, see the
231
+ `xx_format_codes` module documentation."""
128
232
  FormatCodes.__config_console()
129
- return input(FormatCodes.to_ansi(prompt, default_color, brightness_steps))
233
+ user_input = input(FormatCodes.to_ansi(str(prompt), default_color, brightness_steps))
234
+ if reset_ansi:
235
+ _sys.stdout.write("\x1b[0m")
236
+ return user_input
130
237
 
131
238
  @staticmethod
132
239
  def to_ansi(
133
- string: str, default_color: hexa | rgba = None, brightness_steps: int = 20, _default_start: bool = True
240
+ string: str,
241
+ default_color: rgba | hexa = None,
242
+ brightness_steps: int = 20,
243
+ _default_start: bool = True,
134
244
  ) -> str:
135
- result, bg_kwd, color_pref = string, {"bg"}, {"br", "bright"}
136
-
245
+ """Convert the formatting codes inside a string to ANSI formatting.\n
246
+ -------------------------------------------------------------------------
247
+ For exact information about how to use special formatting codes, see the
248
+ `xx_format_codes` module documentation."""
137
249
  if Color.is_valid_rgba(default_color, False):
138
250
  use_default = True
139
251
  elif Color.is_valid_hexa(default_color, False):
@@ -141,8 +253,8 @@ class FormatCodes:
141
253
  else:
142
254
  use_default = False
143
255
  if use_default:
144
- string = COMPILED["*"].sub(r"[\1_|default\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|default|…]`
145
- string = COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` WITH `[…|default|…]`
256
+ string = _COMPILED["*"].sub(r"[\1_|default\2]", string) # REPLACE `[…|*|…]` WITH `[…|_|default|…]`
257
+ string = _COMPILED["*color"].sub(r"[\1default\2]", string) # REPLACE `[…|*color|…]` WITH `[…|default|…]`
146
258
 
147
259
  def is_valid_color(color: str) -> bool:
148
260
  return color in ANSI.color_map or Color.is_valid_rgba(color) or Color.is_valid_hexa(color)
@@ -166,10 +278,9 @@ class FormatCodes:
166
278
  reset_keys = []
167
279
  for k in format_keys:
168
280
  k_lower = k.lower()
169
- k_parts = k_lower.split(":")
170
- k_set = set(k_parts)
171
- if bg_kwd & k_set and len(k_parts) <= 3:
172
- if k_set & color_pref:
281
+ k_set = set(k_lower.split(":"))
282
+ if _PREFIX["BG"] & k_set and len(k_set) <= 3:
283
+ if k_set & _PREFIX["BR"]:
173
284
  for i in range(len(k)):
174
285
  if is_valid_color(k[i:]):
175
286
  reset_keys.extend(["_bg", "_color"])
@@ -181,7 +292,7 @@ class FormatCodes:
181
292
  break
182
293
  elif is_valid_color(k) or any(
183
294
  k_lower.startswith(pref_colon := f"{prefix}:") and is_valid_color(k[len(pref_colon) :])
184
- for prefix in color_pref
295
+ for prefix in _PREFIX["BR"]
185
296
  ):
186
297
  reset_keys.append("_color")
187
298
  else:
@@ -209,38 +320,42 @@ class FormatCodes:
209
320
  + ("" if escaped else "".join(ansi_resets))
210
321
  )
211
322
 
212
- result = "\n".join(COMPILED["format"].sub(replace_keys, line) for line in string.split("\n"))
213
- return (FormatCodes.__get_default_ansi(default_color) if _default_start else "") + result if use_default else result
323
+ string = "\n".join(_COMPILED["formatting"].sub(replace_keys, line) for line in string.split("\n"))
324
+ return (FormatCodes.__get_default_ansi(default_color) if _default_start else "") + string if use_default else string
214
325
 
215
326
  @staticmethod
216
- def escape_ansi(ansi_string: str, escaped_char: str = ANSI.char_esc) -> str:
217
- """Makes the string printable with the ANSI formats visible."""
218
- return ansi_string.replace(ANSI.char, escaped_char)
327
+ def escape_ansi(ansi_string: str) -> str:
328
+ """Escapes all ANSI codes in a string, so they are visible when output to the console."""
329
+ return ansi_string.replace(ANSI.char, ANSI.escaped_char)
219
330
 
220
331
  @staticmethod
221
- @lru_cache(maxsize=64)
222
332
  def __config_console() -> None:
223
- _sys.stdout.flush()
224
- kernel32 = _ctypes.windll.kernel32
225
- h = kernel32.GetStdHandle(-11)
226
- mode = _ctypes.c_ulong()
227
- kernel32.GetConsoleMode(h, _ctypes.byref(mode))
228
- kernel32.SetConsoleMode(h, mode.value | 0x0004)
333
+ """Configure the console to be able to interpret ANSI formatting."""
334
+ if not _CONSOLE_ANSI_CONFIGURED:
335
+ _sys.stdout.flush()
336
+ kernel32 = _ctypes.windll.kernel32
337
+ h = kernel32.GetStdHandle(-11)
338
+ mode = _ctypes.c_ulong()
339
+ kernel32.GetConsoleMode(h, _ctypes.byref(mode))
340
+ kernel32.SetConsoleMode(h, mode.value | 0x0004)
341
+ global _CONSOLE_ANSI_CONFIGURED
342
+ _CONSOLE_ANSI_CONFIGURED = True
229
343
 
230
344
  @staticmethod
231
345
  def __get_default_ansi(
232
346
  default_color: tuple,
233
347
  format_key: str = None,
234
348
  brightness_steps: int = None,
235
- _modifiers: tuple[str, str] = (ANSI.modifier["lighten"], ANSI.modifier["darken"]),
349
+ _modifiers: tuple[str, str] = (ANSI.default_color_modifiers["lighten"], ANSI.default_color_modifiers["darken"]),
236
350
  ) -> str | None:
237
- if not brightness_steps or (format_key and COMPILED["bg?_default"].search(format_key)):
238
- return (ANSI.seq_bg_color if format_key and COMPILED["bg_default"].search(format_key) else ANSI.seq_color).format(
351
+ """Get the `default_color` and lighter/darker versions of it as ANSI code."""
352
+ if not brightness_steps or (format_key and _COMPILED["bg?_default"].search(format_key)):
353
+ return (ANSI.seq_bg_color if format_key and _COMPILED["bg_default"].search(format_key) else ANSI.seq_color).format(
239
354
  *default_color[:3]
240
355
  )
241
356
  if not (format_key in _modifiers[0] or format_key in _modifiers[1]):
242
357
  return None
243
- match = COMPILED["modifier"].match(format_key)
358
+ match = _COMPILED["modifier"].match(format_key)
244
359
  if not match:
245
360
  return None
246
361
  is_bg, modifiers = match.groups()
@@ -264,11 +379,7 @@ class FormatCodes:
264
379
  If `default_color` is not `None`, the text color will be `default_color` if all formats
265
380
  are reset or you can get lighter or darker version of `default_color` (also as BG)"""
266
381
  use_default = default_color and Color.is_valid_rgba(default_color, False)
267
- _format_key, format_key = format_key, ( # NORMALIZE THE FORMAT KEY (+ SAVE ORIGINAL)
268
- "bg:" if "bg" in (parts := format_key.replace(" ", "").lower().split(":")) else ""
269
- ) + ("bright:" if any(x in parts for x in ("bright", "br")) else "") + ":".join(
270
- p for p in parts if p not in ("bg", "bright", "br")
271
- )
382
+ _format_key, format_key = format_key, FormatCodes.__normalize_key(format_key) # NORMALIZE KEY AND SAVE ORIGINAL
272
383
  if use_default:
273
384
  if new_default_color := FormatCodes.__get_default_ansi(default_color, format_key, brightness_steps):
274
385
  return new_default_color
@@ -284,8 +395,8 @@ class FormatCodes:
284
395
  None,
285
396
  )
286
397
  )
287
- rgb_match = _re.match(COMPILED["rgb"], format_key)
288
- hex_match = _re.match(COMPILED["hex"], format_key)
398
+ rgb_match = _re.match(_COMPILED["rgb"], format_key)
399
+ hex_match = _re.match(_COMPILED["hex"], format_key)
289
400
  try:
290
401
  if rgb_match:
291
402
  is_bg = rgb_match.group(1)
@@ -303,3 +414,16 @@ class FormatCodes:
303
414
  except Exception:
304
415
  pass
305
416
  return _format_key
417
+
418
+ @staticmethod
419
+ def __normalize_key(format_key: str) -> str:
420
+ """Normalizes the given format key."""
421
+ k_parts = format_key.replace(" ", "").lower().split(":")
422
+ prefix_str = "".join(
423
+ f"{prefix_key.lower()}:"
424
+ for prefix_key, prefix_values in _PREFIX.items()
425
+ if any(k_part in prefix_values for k_part in k_parts)
426
+ )
427
+ return prefix_str + ":".join(
428
+ part for part in k_parts if part not in {val for values in _PREFIX.values() for val in values}
429
+ )
xulbux/xx_regex.py CHANGED
@@ -9,6 +9,7 @@ Really long regex code presets:
9
9
  `hsla_str` match a HSLA color
10
10
  """
11
11
 
12
+ import regex as _rx
12
13
  import re as _re
13
14
 
14
15
 
@@ -17,25 +18,33 @@ class Regex:
17
18
  @staticmethod
18
19
  def quotes() -> str:
19
20
  """Matches everything inside quotes. (strings)\n
20
- ------------------------------------------------------------------------------------
21
+ --------------------------------------------------------------------------------
21
22
  Will create two named groups:
22
23
  - `quote` the quote type (single or double)
23
24
  - `string` everything inside the found quote pair\n
24
- ------------------------------------------------------------------------------------
25
+ --------------------------------------------------------------------------------
25
26
  Attention: Requires non standard library `regex` not standard library `re`!"""
26
27
  return r'(?P<quote>[\'"])(?P<string>(?:\\.|(?!\g<quote>).)*?)\g<quote>'
27
28
 
28
29
  @staticmethod
29
- def brackets(bracket1: str = "(", bracket2: str = ")", is_group: bool = False) -> str:
30
+ def brackets(bracket1: str = "(", bracket2: str = ")", is_group: bool = False, ignore_in_strings: bool = True) -> str:
30
31
  """Matches everything inside brackets, including other nested brackets.\n
31
- ------------------------------------------------------------------------------------
32
+ --------------------------------------------------------------------------------
33
+ If `is_group` is true, you will be able to reference the matched content as a
34
+ group (e.g. `match.group(…)` or `r'\\…'`).
35
+ If `ignore_in_strings` is true and a bracket is inside a string (e.g. `'...'`
36
+ or `"..."`), it will not be counted as the matching closing bracket.\n
37
+ --------------------------------------------------------------------------------
32
38
  Attention: Requires non standard library `regex` not standard library `re`!"""
33
39
  g, b1, b2 = (
34
40
  "" if is_group else "?:",
35
- _re.escape(bracket1) if len(bracket1) == 1 else bracket1,
36
- _re.escape(bracket2) if len(bracket2) == 1 else bracket2,
41
+ _rx.escape(bracket1) if len(bracket1) == 1 else bracket1,
42
+ _rx.escape(bracket2) if len(bracket2) == 1 else bracket2,
37
43
  )
38
- return rf'{b1}\s*({g}(?:[^{b1}{b2}"\']|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|{b1}(?:[^{b1}{b2}"\']|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|(?R))*{b2})*)\s*{b2}'
44
+ if ignore_in_strings:
45
+ return rf'{b1}\s*({g}(?:[^{b1}{b2}"\']|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|{b1}(?:[^{b1}{b2}"\']|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|(?R))*{b2})*)\s*{b2}'
46
+ else:
47
+ return rf"{b1}\s*({g}(?:[^{b1}{b2}]|{b1}(?:[^{b1}{b2}]|(?R))*{b2})*)\s*{b2}"
39
48
 
40
49
  @staticmethod
41
50
  def outside_strings(pattern: str = r".*") -> str:
@@ -48,22 +57,27 @@ class Regex:
48
57
  ignore_pattern: str = "",
49
58
  is_group: bool = False,
50
59
  ) -> str:
51
- """Matches everything except `disallowed_pattern`, unless the `disallowed_pattern` is found inside a string (`'...'` or `"..."`).\n
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. `match.group(int)` or `r'\\int'`)."""
60
+ """Matches everything except `disallowed_pattern`, unless the `disallowed_pattern`
61
+ is found inside a string (`'...'` or `"..."`).\n
62
+ ------------------------------------------------------------------------------------
63
+ The `ignore_pattern` is just always ignored. For example if `disallowed_pattern` is
64
+ `>` and `ignore_pattern` is `->`, the `->`-arrows will be allowed, even though they
65
+ have `>` in them.
66
+ If `is_group` is true, you will be able to reference the matched content as a group
67
+ (e.g. `match.group(…)` or `r'\\…'`)."""
56
68
  return rf'({"" if is_group else "?:"}(?:(?!{ignore_pattern}).)*(?:(?!{Regex.outside_strings(disallowed_pattern)}).)*)'
57
69
 
58
70
  @staticmethod
59
71
  def func_call(func_name: str = None) -> str:
60
- """Match a function call
61
- - `1` function name
62
- - `2` the function's arguments\n
72
+ """Match a function call, and get back two groups:
73
+ 1. function name
74
+ 2. the function's arguments\n
63
75
  If no `func_name` is given, it will match any function call.\n
64
- ------------------------------------------------------------------------------------
76
+ --------------------------------------------------------------------------------
65
77
  Attention: Requires non standard library `regex` not standard library `re`!"""
66
- return r"(?<=\b)(" + (func_name if func_name else r"[\w_]+") + r")\s*" + Regex.brackets("(", ")", is_group=True)
78
+ return (
79
+ r"(?<=\b)(" + (r"[\w_]+" if func_name is None else func_name) + r")\s*" + Regex.brackets("(", ")", is_group=True)
80
+ )
67
81
 
68
82
  @staticmethod
69
83
  def rgba_str(fix_sep: str = ",", allow_alpha: bool = True) -> str:
@@ -76,11 +90,10 @@ class Regex:
76
90
  - `(r, g, b, a)` (if `allow_alpha=True`)
77
91
  - `r, g, b`
78
92
  - `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)
93
+ #### Valid ranges:
94
+ - `r` 0-255 (int: red)
95
+ - `g` 0-255 (int: green)
96
+ - `b` 0-255 (int: blue)
84
97
  - `a` 0-1 (float: opacity)\n
85
98
  ----------------------------------------------------------------------------
86
99
  If the `fix_sep` is set to nothing, any char that is not a letter or number
@@ -112,11 +125,10 @@ class Regex:
112
125
  - `(h, s, l, a)` (if `allow_alpha=True`)
113
126
  - `h, s, l`
114
127
  - `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)
128
+ #### Valid ranges:
129
+ - `h` 0-360 (int: hue)
130
+ - `s` 0-100 (int: saturation)
131
+ - `l` 0-100 (int: lightness)
120
132
  - `a` 0-1 (float: opacity)\n
121
133
  ----------------------------------------------------------------------------
122
134
  If the `fix_sep` is set to nothing, any char that is not a letter or number
@@ -146,9 +158,8 @@ class Regex:
146
158
  - `RGBA` (if `allow_alpha=True`)
147
159
  - `RRGGBB`
148
160
  - `RRGGBBAA` (if `allow_alpha=True`)\n
149
- ----------------------------------------------------------------------
150
- ### Valid ranges:
151
- each channel from 0-9 and A-F (case insensitive)"""
161
+ #### Valid ranges:
162
+ every channel from 0-9 and A-F (case insensitive)"""
152
163
  return (
153
164
  r"(?i)^(?:#|0x)?[0-9A-F]{8}|[0-9A-F]{6}|[0-9A-F]{4}|[0-9A-F]{3}$"
154
165
  if allow_alpha