xulbux 1.5.5__py3-none-any.whl → 1.5.7__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_cmd.py CHANGED
@@ -20,14 +20,12 @@ You can also use special formatting codes directly inside the log message to cha
20
20
  For more detailed information about formatting codes, see the the `xx_format_codes` description.
21
21
  """
22
22
 
23
-
24
23
  from ._consts_ import DEFAULT, CHARS
25
24
  from .xx_format_codes import *
26
25
  from .xx_string import *
27
26
  from .xx_color import *
28
27
 
29
28
  from contextlib import suppress
30
- import subprocess as _subprocess
31
29
  import pyperclip as _pyperclip
32
30
  import keyboard as _keyboard
33
31
  import getpass as _getpass
@@ -38,12 +36,10 @@ import sys as _sys
38
36
  import os as _os
39
37
 
40
38
 
41
-
42
-
43
39
  class Cmd:
44
40
 
45
41
  @staticmethod
46
- def get_args(find_args:dict) -> dict:
42
+ def get_args(find_args: dict) -> dict[str, dict[str, any]]:
47
43
  args = _sys.argv[1:]
48
44
  results = {}
49
45
  for arg_key, arg_group in find_args.items():
@@ -53,47 +49,71 @@ class Cmd:
53
49
  if arg in args:
54
50
  exists = True
55
51
  arg_index = args.index(arg)
56
- if arg_index + 1 < len(args) and not args[arg_index + 1].startswith('-'):
52
+ if arg_index + 1 < len(args) and not args[arg_index + 1].startswith("-"):
57
53
  value = String.to_type(args[arg_index + 1])
58
54
  break
59
- results[arg_key] = {'exists': exists, 'value': value}
55
+ results[arg_key] = {"exists": exists, "value": value}
60
56
  return results
61
57
 
62
- def w() -> int: return getattr(_shutil.get_terminal_size(), 'columns', 80)
63
- def h() -> int: return getattr(_shutil.get_terminal_size(), 'lines', 24)
64
- def wh() -> tuple[int,int]: return Cmd.w(), Cmd.h()
65
- def user() -> str: return _os.getenv('USER') or _os.getenv('USERNAME') or _getpass.getuser()
58
+ def w() -> int:
59
+ return getattr(_shutil.get_terminal_size(), "columns", 80)
60
+
61
+ def h() -> int:
62
+ return getattr(_shutil.get_terminal_size(), "lines", 24)
63
+
64
+ def wh() -> tuple[int, int]:
65
+ return Cmd.w(), Cmd.h()
66
+
67
+ def user() -> str:
68
+ return _os.getenv("USER") or _os.getenv("USERNAME") or _getpass.getuser()
66
69
 
67
- @staticmethod
68
70
  def is_admin() -> bool:
69
71
  try:
70
- if _os.name == 'nt':
72
+ if _os.name == "nt":
71
73
  return _ctypes.windll.shell32.IsUserAnAdmin() != 0
72
- elif _os.name == 'posix':
74
+ elif _os.name == "posix":
73
75
  return _os.geteuid() == 0
74
76
  else:
75
77
  return False
76
- except:
78
+ except Exception:
77
79
  return False
78
80
 
79
81
  @staticmethod
80
- def pause_exit(pause:bool = False, exit:bool = False, last_prompt:object = '', exit_code:int = 0, reset_ansi:bool = False) -> None:
82
+ def pause_exit(
83
+ pause: bool = False,
84
+ exit: bool = False,
85
+ prompt: object = "",
86
+ exit_code: int = 0,
87
+ reset_ansi: bool = False,
88
+ ) -> None:
81
89
  """Will print the `last_prompt` and then pause the program if `pause` is set<br>
82
- to `True` and after the pause, exit the program if `exit` is set to `True`."""
83
- print(last_prompt, end='', flush=True)
84
- if reset_ansi: FormatCodes.print('[_]', end='')
85
- if pause: _keyboard.read_event()
86
- if exit: _sys.exit(exit_code)
90
+ to `True` and after the pause, exit the program if `exit` is set to `True`.
91
+ """
92
+ print(prompt, end="", flush=True)
93
+ if reset_ansi:
94
+ FormatCodes.print("[_]", end="")
95
+ if pause:
96
+ _keyboard.read_event()
97
+ if exit:
98
+ _sys.exit(exit_code)
87
99
 
88
- @staticmethod
89
100
  def cls() -> None:
90
101
  """Will clear the console in addition to completely resetting the ANSI formats."""
91
- if _shutil.which('cls'): _os.system('cls')
92
- elif _shutil.which('clear'): _os.system('clear')
93
- print('\033[0m', end='', flush=True)
102
+ if _shutil.which("cls"):
103
+ _os.system("cls")
104
+ elif _shutil.which("clear"):
105
+ _os.system("clear")
106
+ print("\033[0m", end="", flush=True)
94
107
 
95
108
  @staticmethod
96
- def log(title:str, prompt:object, start:str = '', end:str = '\n', title_bg_color:hexa|rgba = None, default_color:hexa|rgba = None) -> None:
109
+ def log(
110
+ title: str,
111
+ prompt: object = "",
112
+ start: str = "",
113
+ end: str = "\n",
114
+ title_bg_color: hexa | rgba = None,
115
+ default_color: hexa | rgba = None,
116
+ ) -> None:
97
117
  """Will print a formatted log message:<br>
98
118
  `title` -⠀the title of the log message (e.g. `DEBUG`, `WARN`, `FAIL`, etc.)<br>
99
119
  `prompt` -⠀the log message<br>
@@ -103,138 +123,259 @@ class Cmd:
103
123
  `default_color` -⠀the default text color of the `prompt`\n
104
124
  --------------------------------------------------------------------------------
105
125
  The log message supports special formatting codes. For more detailed<br>
106
- information about formatting codes, see `xx_format_codes` class description."""
107
- title_color = '_color' if not title_bg_color else Color.text_color_for_on_bg(title_bg_color)
108
- if title: FormatCodes.print(f'{start} [bold][{title_color}]{f"[BG:{title_bg_color}]" if title_bg_color else ""} {title.upper()}: [_]\t{f"[{default_color}]" if default_color else ""}{str(prompt)}[_]', default_color, end=end)
109
- else: FormatCodes.print(f'{start} {f"[{default_color}]" if default_color else ""}{str(prompt)}[_]', default_color, end=end)
126
+ information about formatting codes, see `xx_format_codes` class description.
127
+ """
128
+ title_color = "_color" if not title_bg_color else Color.text_color_for_on_bg(title_bg_color)
129
+ if title:
130
+ FormatCodes.print(
131
+ f'{start} [bold][{title_color}]{f"[BG:{title_bg_color}]" if title_bg_color else ""} {title.upper()}: [_]\t{f"[{default_color}]" if default_color else ""}{str(prompt)}[_]',
132
+ default_color=default_color,
133
+ end=end,
134
+ )
135
+ else:
136
+ FormatCodes.print(
137
+ f'{start} {f"[{default_color}]" if default_color else ""}{str(prompt)}[_]',
138
+ default_color=default_color,
139
+ end=end,
140
+ )
110
141
 
111
142
  @staticmethod
112
- def debug(prompt:object = 'Point in program reached.', active:bool = True, start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['yellow'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = False) -> None:
143
+ def debug(
144
+ prompt: object = "Point in program reached.",
145
+ active: bool = True,
146
+ start: str = "\n",
147
+ end: str = "\n\n",
148
+ title_bg_color: hexa | rgba = DEFAULT.color["yellow"],
149
+ default_color: hexa | rgba = DEFAULT.text_color,
150
+ pause: bool = False,
151
+ exit: bool = False,
152
+ ) -> None:
113
153
  """A preset for `log()`: `DEBUG` log message with the options to pause<br>
114
154
  at the message and exit the program after the message was printed."""
115
155
  if active:
116
- Cmd.log('DEBUG', prompt, start, end, title_bg_color, default_color)
156
+ Cmd.log("DEBUG", prompt, start, end, title_bg_color, default_color)
117
157
  Cmd.pause_exit(pause, exit)
118
158
 
119
159
  @staticmethod
120
- def info(prompt:object = 'Program running.', start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['blue'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = False) -> None:
160
+ def info(
161
+ prompt: object = "Program running.",
162
+ start: str = "\n",
163
+ end: str = "\n\n",
164
+ title_bg_color: hexa | rgba = DEFAULT.color["blue"],
165
+ default_color: hexa | rgba = DEFAULT.text_color,
166
+ pause: bool = False,
167
+ exit: bool = False,
168
+ ) -> None:
121
169
  """A preset for `log()`: `INFO` log message with the options to pause<br>
122
170
  at the message and exit the program after the message was printed."""
123
- Cmd.log('INFO', prompt, start, end, title_bg_color, default_color)
171
+ Cmd.log("INFO", prompt, start, end, title_bg_color, default_color)
124
172
  Cmd.pause_exit(pause, exit)
125
173
 
126
174
  @staticmethod
127
- def done(prompt:object = 'Program finished.', start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['teal'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = False) -> None:
175
+ def done(
176
+ prompt: object = "Program finished.",
177
+ start: str = "\n",
178
+ end: str = "\n\n",
179
+ title_bg_color: hexa | rgba = DEFAULT.color["teal"],
180
+ default_color: hexa | rgba = DEFAULT.text_color,
181
+ pause: bool = False,
182
+ exit: bool = False,
183
+ ) -> None:
128
184
  """A preset for `log()`: `DONE` log message with the options to pause<br>
129
185
  at the message and exit the program after the message was printed."""
130
- Cmd.log('DONE', prompt, start, end, title_bg_color, default_color)
186
+ Cmd.log("DONE", prompt, start, end, title_bg_color, default_color)
131
187
  Cmd.pause_exit(pause, exit)
132
188
 
133
189
  @staticmethod
134
- def warn(prompt:object = 'Important message.', start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['orange'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = False) -> None:
190
+ def warn(
191
+ prompt: object = "Important message.",
192
+ start: str = "\n",
193
+ end: str = "\n\n",
194
+ title_bg_color: hexa | rgba = DEFAULT.color["orange"],
195
+ default_color: hexa | rgba = DEFAULT.text_color,
196
+ pause: bool = False,
197
+ exit: bool = False,
198
+ ) -> None:
135
199
  """A preset for `log()`: `WARN` log message with the options to pause<br>
136
200
  at the message and exit the program after the message was printed."""
137
- Cmd.log('WARN', prompt, start, end, title_bg_color, default_color)
201
+ Cmd.log("WARN", prompt, start, end, title_bg_color, default_color)
138
202
  Cmd.pause_exit(pause, exit)
139
203
 
140
204
  @staticmethod
141
- def fail(prompt:object = 'Program error.', start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['red'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = True, reset_ansi=True) -> None:
205
+ def fail(
206
+ prompt: object = "Program error.",
207
+ start: str = "\n",
208
+ end: str = "\n\n",
209
+ title_bg_color: hexa | rgba = DEFAULT.color["red"],
210
+ default_color: hexa | rgba = DEFAULT.text_color,
211
+ pause: bool = False,
212
+ exit: bool = True,
213
+ reset_ansi=True,
214
+ ) -> None:
142
215
  """A preset for `log()`: `FAIL` log message with the options to pause<br>
143
216
  at the message and exit the program after the message was printed."""
144
- Cmd.log('FAIL', prompt, start, end, title_bg_color, default_color)
217
+ Cmd.log("FAIL", prompt, start, end, title_bg_color, default_color)
145
218
  Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
146
219
 
147
220
  @staticmethod
148
- def exit(prompt:object = 'Program ended.', start:str = '\n', end:str = '\n\n', title_bg_color:hexa|rgba = DEFAULT.color['magenta'], default_color:hexa|rgba = DEFAULT.text_color, pause:bool = False, exit:bool = True, reset_ansi=True) -> None:
221
+ def exit(
222
+ prompt: object = "Program ended.",
223
+ start: str = "\n",
224
+ end: str = "\n\n",
225
+ title_bg_color: hexa | rgba = DEFAULT.color["magenta"],
226
+ default_color: hexa | rgba = DEFAULT.text_color,
227
+ pause: bool = False,
228
+ exit: bool = True,
229
+ reset_ansi=True,
230
+ ) -> None:
149
231
  """A preset for `log()`: `EXIT` log message with the options to pause<br>
150
232
  at the message and exit the program after the message was printed."""
151
- Cmd.log('EXIT', prompt, start, end, title_bg_color, default_color)
233
+ Cmd.log("EXIT", prompt, start, end, title_bg_color, default_color)
152
234
  Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
153
235
 
154
236
  @staticmethod
155
- def input(prompt:object = '', default_color:hexa|rgba = DEFAULT.color['cyan']) -> None:
156
- """Acts like a standard Python `input()` but the prompt can be formatted with special formatting codes.<br>
157
- For more detailed information about formatting codes, see the `xx_format_codes` description."""
158
- return input(FormatCodes.to_ansi(str(prompt), default_color))
159
-
160
- @staticmethod
161
- def confirm(prompt:object = 'Do you want to continue?', start = '\n', end = '\n', default_color:hexa|rgba = DEFAULT.color['cyan'], default_is_yes:bool = True) -> None:
237
+ def confirm(
238
+ prompt: object = "Do you want to continue?",
239
+ start="\n",
240
+ end="\n",
241
+ default_color: hexa | rgba = DEFAULT.color["cyan"],
242
+ default_is_yes: bool = True,
243
+ ) -> bool:
162
244
  """Ask a yes/no question.\n
163
245
  -----------------------------------------------------------------------------------
164
246
  The question can be formatted with special formatting codes. For more detailed<br>
165
- information about formatting codes, see the `xx_format_codes` description."""
166
- confirmed = input(FormatCodes.to_ansi(f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )', default_color)).strip().lower() in (('', 'y', 'yes') if default_is_yes else ('y', 'yes'))
167
- if end: Cmd.log('', '') if end == '\n' else Cmd.log('', end[1:]) if end.startswith('\n') else Cmd.log('', end)
247
+ information about formatting codes, see the `xx_format_codes` description.
248
+ """
249
+ confirmed = input(
250
+ FormatCodes.to_ansi(
251
+ f'{start} {str(prompt)} [_|dim](({"Y" if default_is_yes else "y"}/{"n" if default_is_yes else "N"}): )',
252
+ default_color,
253
+ )
254
+ ).strip().lower() in (("", "y", "yes") if default_is_yes else ("y", "yes"))
255
+ if end:
256
+ Cmd.log("", end, end="")
168
257
  return confirmed
169
258
 
170
259
  @staticmethod
171
- def restricted_input(prompt:object = '', allowed_chars:str = CHARS.all, min_length:int = None, max_length:int = None, mask_char:str = None) -> str|None:
260
+ def restricted_input(
261
+ prompt: object = "",
262
+ allowed_chars: str = CHARS.all,
263
+ min_len: int = None,
264
+ max_len: int = None,
265
+ mask_char: str = None,
266
+ reset_ansi: bool = True,
267
+ ) -> str | None:
172
268
  """Acts like a standard Python `input()` with the advantage, that you can specify:
173
269
  - what text characters the user is allowed to type and
174
270
  - the minimum and/or maximum length of the users input
175
- - optional mask character (hide user input, e.g. for passwords)\n
271
+ - optional mask character (hide user input, e.g. for passwords)
272
+ - reset the ANSI formatting codes after the user continues\n
176
273
  -----------------------------------------------------------------------------------
177
274
  The input can be formatted with special formatting codes. For more detailed<br>
178
- information about formatting codes, see the `xx_format_codes` description."""
179
- print(prompt, end='', flush=True)
180
- result, select_all, last_line_count, last_console_width = '', False, 1, 0
181
- def filter_pasted_text(text:str) -> str:
182
- if allowed_chars == CHARS.all: return text
183
- return ''.join(char for char in text if char in allowed_chars)
184
- def update_display(console_width:int) -> None:
275
+ information about formatting codes, see the `xx_format_codes` description.
276
+ """
277
+ FormatCodes.print(prompt, end="", flush=True)
278
+ result = ""
279
+ select_all = False
280
+ last_line_count = 1
281
+ last_console_width = 0
282
+
283
+ def update_display(console_width: int) -> None:
185
284
  nonlocal select_all, last_line_count, last_console_width
186
- lines = String.split_every_chars(str(prompt) + (mask_char * len(result) if mask_char else result), console_width)
285
+ lines = String.split_count(
286
+ str(prompt) + (mask_char * len(result) if mask_char else result),
287
+ console_width,
288
+ )
187
289
  line_count = len(lines)
188
290
  if (line_count > 1 or line_count < last_line_count) and not last_line_count == 1:
189
- if last_console_width > console_width: line_count *= 2
190
- for _ in range(line_count if line_count < last_line_count and not line_count > last_line_count else line_count - 2 if line_count > last_line_count else line_count - 1):
191
- _sys.stdout.write('\033[2K\r\033[A')
291
+ if last_console_width > console_width:
292
+ line_count *= 2
293
+ for _ in range(
294
+ line_count
295
+ if line_count < last_line_count and not line_count > last_line_count
296
+ else (line_count - 2 if line_count > last_line_count else line_count - 1)
297
+ ):
298
+ _sys.stdout.write("\033[2K\r\033[A")
192
299
  prompt_len = len(str(prompt)) if prompt else 0
193
- prompt_str, input_str = lines[0][:prompt_len], lines[0][prompt_len:] if len(lines) == 1 else '\n'.join([lines[0][prompt_len:]] + lines[1:]) # SEPARATE THE PROMPT AND THE INPUT
194
- _sys.stdout.write('\033[2K\r' + prompt_str + ('\033[7m' if select_all else '') + input_str + '\033[0m')
300
+ prompt_str, input_str = lines[0][:prompt_len], (
301
+ lines[0][prompt_len:] if len(lines) == 1 else "\n".join([lines[0][prompt_len:]] + lines[1:])
302
+ ) # SEPARATE THE PROMPT AND THE INPUT
303
+ _sys.stdout.write(
304
+ "\033[2K\r" + FormatCodes.to_ansi(prompt_str) + ("\033[7m" if select_all else "") + input_str + "\033[27m"
305
+ )
195
306
  last_line_count, last_console_width = line_count, console_width
307
+
308
+ def handle_enter():
309
+ if min_len is not None and len(result) < min_len:
310
+ return False
311
+ FormatCodes.print("[_]" if reset_ansi else "", flush=True)
312
+ return True
313
+
314
+ def handle_backspace_delete():
315
+ nonlocal result, select_all
316
+ if select_all:
317
+ result, select_all = "", False
318
+ elif result and event.name == "backspace":
319
+ result = result[:-1]
320
+ update_display(Cmd.w())
321
+
322
+ def handle_paste():
323
+ nonlocal result, select_all
324
+ if select_all:
325
+ result, select_all = "", False
326
+ filtered_text = "".join(char for char in _pyperclip.paste() if allowed_chars == CHARS.all or char in allowed_chars)
327
+ if max_len is None or len(result) + len(filtered_text) <= max_len:
328
+ result += filtered_text
329
+ update_display(Cmd.w())
330
+
331
+ def handle_select_all():
332
+ nonlocal select_all
333
+ select_all = True
334
+ update_display(Cmd.w())
335
+
336
+ def handle_copy():
337
+ nonlocal select_all
338
+ with suppress(KeyboardInterrupt):
339
+ select_all = False
340
+ update_display(Cmd.w())
341
+ _pyperclip.copy(result)
342
+
343
+ def handle_character_input():
344
+ nonlocal result
345
+ if (allowed_chars == CHARS.all or event.name in allowed_chars) and (max_len is None or len(result) < max_len):
346
+ result += event.name
347
+ update_display(Cmd.w())
348
+
196
349
  while True:
197
350
  event = _keyboard.read_event()
198
- if event.event_type == 'down':
199
- if event.name == 'enter':
200
- if min_length is not None and len(result) < min_length:
201
- continue
202
- print()
203
- return result.rstrip('\n')
204
- elif event.name in ('backspace', 'delete', 'entf'):
205
- if select_all: result, select_all = '', False
206
- elif result and event.name == 'backspace':
207
- result = result[:-1]
208
- update_display(Cmd.w())
209
- elif (event.name == 'v' and _keyboard.is_pressed('ctrl')) or _mouse.is_pressed('right'):
210
- if select_all: result, select_all = '', False
211
- filtered_text = filter_pasted_text(_pyperclip.paste())
212
- if max_length is None or len(result) + len(filtered_text) <= max_length:
213
- result += filtered_text
214
- update_display(Cmd.w())
215
- elif event.name == 'a' and _keyboard.is_pressed('ctrl'):
216
- select_all = True
217
- update_display(Cmd.w())
218
- elif event.name == 'c' and _keyboard.is_pressed('ctrl') and select_all:
219
- with suppress(KeyboardInterrupt): # PREVENT CTRL+C FROM RAISING A `KeyboardInterrupt` EXCEPTION
220
- select_all = False
221
- update_display(Cmd.w())
222
- _pyperclip.copy(result)
223
- elif event.name == 'esc':
224
- return
225
- elif event.name == 'space':
226
- if (allowed_chars == CHARS.all or ' ' in allowed_chars) and (max_length is None or len(result) < max_length):
227
- result += ' '
228
- update_display(Cmd.w())
351
+ if event.event_type == "down":
352
+ if event.name == "enter" and handle_enter():
353
+ return result.rstrip("\n")
354
+ elif event.name in ("backspace", "delete", "entf"):
355
+ handle_backspace_delete()
356
+ elif (event.name == "v" and _keyboard.is_pressed("ctrl")) or _mouse.is_pressed("right"):
357
+ handle_paste()
358
+ elif event.name == "a" and _keyboard.is_pressed("ctrl"):
359
+ handle_select_all()
360
+ elif event.name == "c" and _keyboard.is_pressed("ctrl") and select_all:
361
+ handle_copy()
362
+ elif event.name == "esc":
363
+ return None
364
+ elif event.name == "space":
365
+ handle_character_input()
229
366
  elif len(event.name) == 1:
230
- if (allowed_chars == CHARS.all or event.name in allowed_chars) and (max_length is None or len(result) < max_length):
231
- result += event.name
232
- update_display(Cmd.w())
233
- else: # ANY DISALLOWED OR NON-DEFINED KEY PRESSED
367
+ handle_character_input()
368
+ else:
234
369
  select_all = False
235
370
  update_display(Cmd.w())
236
371
 
237
372
  @staticmethod
238
- def pwd_input(prompt:object = 'Password: ', allowed_chars:str = CHARS.standard_ascii, min_length:int = None, max_length:int = None) -> str:
373
+ def pwd_input(
374
+ prompt: object = "Password: ",
375
+ allowed_chars: str = CHARS.standard_ascii,
376
+ min_len: int = None,
377
+ max_len: int = None,
378
+ _reset_ansi: bool = True,
379
+ ) -> str:
239
380
  """Password input that masks the entered characters with asterisks."""
240
- return Cmd.restricted_input(prompt, allowed_chars, min_length, max_length, mask_char='*')
381
+ return Cmd.restricted_input(prompt, allowed_chars, min_len, max_len, "*", _reset_ansi)
xulbux/xx_code.py CHANGED
@@ -5,24 +5,16 @@ from .xx_data import *
5
5
  import regex as _rx
6
6
 
7
7
 
8
-
9
-
10
8
  class Code:
11
9
 
12
10
  @staticmethod
13
- def normalize_spaces(string:str, tab_spaces:int = 4) -> str:
14
- """Replaces all special space characters with normal spaces.<br>
15
- Also replaces tab characters with `tab_spaces` spaces."""
16
- return string.replace('\t', ' ' * tab_spaces).replace('\u2000', ' ').replace('\u2001', ' ').replace('\u2002', ' ').replace('\u2003', ' ').replace('\u2004', ' ').replace('\u2005', ' ').replace('\u2006', ' ').replace('\u2007', ' ').replace('\u2008', ' ').replace('\u2009', ' ').replace('\u200A', ' ')
17
-
18
- @staticmethod
19
- def add_indent(code:str, indent:int) -> str:
11
+ def add_indent(code: str, indent: int) -> str:
20
12
  """Adds `indent` spaces at the beginning of each line."""
21
- indented_lines = [' ' * indent + line for line in code.splitlines()]
22
- return '\n'.join(indented_lines)
13
+ indented_lines = [" " * indent + line for line in code.splitlines()]
14
+ return "\n".join(indented_lines)
23
15
 
24
16
  @staticmethod
25
- def get_tab_spaces(code:str) -> int:
17
+ def get_tab_spaces(code: str) -> int:
26
18
  """Will try to get the amount of spaces used for indentation."""
27
19
  code_lines = String.get_string_lines(code, remove_empty_lines=True)
28
20
  indents = [len(line) - len(line.lstrip()) for line in code_lines]
@@ -30,42 +22,50 @@ class Code:
30
22
  return min(non_zero_indents) if non_zero_indents else 0
31
23
 
32
24
  @staticmethod
33
- def change_tab_size(code:str, new_tab_size:int, remove_empty_lines:bool = False) -> str:
25
+ def change_tab_size(code: str, new_tab_size: int, remove_empty_lines: bool = False) -> str:
34
26
  """Replaces all tabs with `new_tab_size` spaces.<br>
35
- If `remove_empty_lines` is `True`, empty lines will be removed in the process."""
27
+ If `remove_empty_lines` is `True`, empty lines will be removed in the process.
28
+ """
36
29
  code_lines = String.get_string_lines(code, remove_empty_lines=True)
37
30
  lines = code_lines if remove_empty_lines else String.get_string_lines(code)
38
31
  tab_spaces = Code.get_tab_spaces(code)
39
32
  if (tab_spaces == new_tab_size) or tab_spaces == 0:
40
33
  if remove_empty_lines:
41
- return '\n'.join(code_lines)
34
+ return "\n".join(code_lines)
42
35
  return code
43
36
  result = []
44
37
  for line in lines:
45
38
  stripped = line.lstrip()
46
39
  indent_level = (len(line) - len(stripped)) // tab_spaces
47
- new_indent = ' ' * (indent_level * new_tab_size)
40
+ new_indent = " " * (indent_level * new_tab_size)
48
41
  result.append(new_indent + stripped)
49
- return '\n'.join(result)
42
+ return "\n".join(result)
50
43
 
51
44
  @staticmethod
52
- def get_func_calls(code:str) -> list:
45
+ def get_func_calls(code: str) -> list:
53
46
  """Will try to get all function calls and return them as a list."""
54
- funcs, nested_func_calls = _rx.findall(r'(?i)' + Regex.func_call(), code), []
47
+ funcs = _rx.findall(r"(?i)" + Regex.func_call(), code)
48
+ nested_func_calls = []
55
49
  for _, func_attrs in funcs:
56
- nested_calls = _rx.findall(r'(?i)' + Regex.func_call(), func_attrs)
50
+ nested_calls = _rx.findall(r"(?i)" + Regex.func_call(), func_attrs)
57
51
  if nested_calls:
58
52
  nested_func_calls.extend(nested_calls)
59
53
  return Data.remove_duplicates(funcs + nested_func_calls)
60
54
 
61
55
  @staticmethod
62
- def is_js(code:str, funcs:list = ['__', '$t', '$lang']) -> bool:
56
+ def is_js(code: str, funcs: list = ["__", "$t", "$lang"]) -> bool:
63
57
  """Will check if the code is likely to be JavaScript."""
64
- funcs = '|'.join(funcs)
65
- js_pattern = _rx.compile(Regex.outside_strings(r'''^(?:
58
+ funcs = "|".join(funcs)
59
+ js_pattern = _rx.compile(
60
+ Regex.outside_strings(
61
+ r"""^(?:
66
62
  (\$[\w_]+)\s* # JQUERY-STYLE VARIABLES
67
63
  |(\$[\w_]+\s*\() # JQUERY-STYLE FUNCTION CALLS
68
- |((''' + funcs + r')' + Regex.brackets('()') + r'''\s*) # PREDEFINED FUNCTION CALLS
64
+ |(("""
65
+ + funcs
66
+ + r")"
67
+ + Regex.brackets("()")
68
+ + r"""\s*) # PREDEFINED FUNCTION CALLS
69
69
  |(\bfunction\s*\() # FUNCTION DECLARATIONS
70
70
  |(\b(var|let|const)\s+[\w_]+\s*=) # VARIABLE DECLARATIONS
71
71
  |(\b(if|for|while|switch)\s*\() # CONTROL STRUCTURES
@@ -98,5 +98,8 @@ class Code:
98
98
  |(\bdelete\b) # DELETE OPERATOR
99
99
  |(\btypeof\b) # TYPEOF OPERATOR
100
100
  |(\bvoid\b) # VOID OPERATOR
101
- )[\s\S]*$'''), _rx.VERBOSE | _rx.IGNORECASE)
101
+ )[\s\S]*$"""
102
+ ),
103
+ _rx.VERBOSE | _rx.IGNORECASE,
104
+ )
102
105
  return bool(js_pattern.fullmatch(code))