xulbux 1.5.5__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.
xulbux/__help__.py ADDED
@@ -0,0 +1,74 @@
1
+ try: from ._consts_ import DEFAULT
2
+ except: from _consts_ import DEFAULT
3
+ try: from .xx_format_codes import *
4
+ except: from xx_format_codes import *
5
+ try: from .xx_cmd import *
6
+ except: from xx_cmd import *
7
+
8
+ import os as _os
9
+
10
+
11
+
12
+
13
+ def get_version(file:str = '__init__.py', var:str = '__version__') -> str:
14
+ try:
15
+ from . import var
16
+ return var
17
+ except ImportError:
18
+ init_path = _os.path.join(_os.path.dirname(__file__), file)
19
+ if _os.path.isfile(init_path):
20
+ with open(init_path, encoding='utf-8') as f:
21
+ for line in f:
22
+ if line.startswith(var): return line.split('=')[-1].strip().strip('\'"')
23
+ return 'unknown'
24
+
25
+ def help():
26
+ """Show some info about the library, with a brief explanation of how to use it."""
27
+ color = {
28
+ 'lib': DEFAULT.color['ice'],
29
+ 'import': DEFAULT.color['red'],
30
+ 'class': DEFAULT.color['lavender'],
31
+ 'types': DEFAULT.color['lightblue'],
32
+ 'punctuators': DEFAULT.color['darkgray'],
33
+ }
34
+ FormatCodes.print(
35
+ rf''' [_|b|#7075FF] __ __
36
+ [b|#7075FF] _ __ __ __/ / / /_ __ ___ __
37
+ [b|#7075FF] | |/ // / / / / / __ \/ / / | |/ /
38
+ [b|#7075FF] > , </ /_/ / /_/ /_/ / /_/ /> , <
39
+ [b|#7075FF]/_/|_|\____/\__/\____/\____//_/|_| [*|BG:{DEFAULT.color['gray']}|#000] v[b]{get_version()} [*]
40
+
41
+ [i|{DEFAULT.color['coral']}]A TON OF COOL FUNCTIONS, YOU NEED![*]
42
+
43
+ [b|#75A2FF]Usage:[*]
44
+ [{color['punctuators']}]# GENERAL LIBRARY[*]
45
+ [{color['import']}]import [{color['lib']}]XulbuX [{color['import']}]as [{color['lib']}]xx[*]
46
+ [{color['punctuators']}]# CUSTOM TYPES[*]
47
+ [{color['import']}]from [{color['lib']}]XulbuX [{color['import']}]import [{color['lib']}]rgba[{color['punctuators']}], [{color['lib']}]hsla[{color['punctuators']}], [{color['lib']}]hexa[*]
48
+
49
+ [b|#75A2FF]Includes:[*]
50
+ [dim](•) CUSTOM TYPES:
51
+ [dim](•) [{color['class']}]rgba[{color['punctuators']}]/([i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]float[_|{color['punctuators']}])[*]
52
+ [dim](•) [{color['class']}]hsla[{color['punctuators']}]/([i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]int[_|{color['punctuators']}],[i|{color['types']}]float[_|{color['punctuators']}])[*]
53
+ [dim](•) [{color['class']}]hexa[{color['punctuators']}]/([i|{color['types']}]str[_|{color['punctuators']}])[*]
54
+ [dim](•) PATH OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Path[*]
55
+ [dim](•) FILE OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]File[*]
56
+ [dim](•) JSON FILE OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Json[*]
57
+ [dim](•) SYSTEM ACTIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]System[*]
58
+ [dim](•) MANAGE ENVIRONMENT VARS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Env_vars[*]
59
+ [dim](•) CMD LOG AND ACTIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Cmd[*]
60
+ [dim](•) PRETTY PRINTING [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]FormatCodes[*]
61
+ [dim](•) COLOR OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Color[*]
62
+ [dim](•) DATA OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Data[*]
63
+ [dim](•) STR OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]String[*]
64
+ [dim](•) CODE STRING OPERATIONS [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Code[*]
65
+ [dim](•) REGEX PATTERN TEMPLATES [{color['lib']}]xx[{color['punctuators']}].[{color['class']}]Regex[*]
66
+ [_]
67
+ [dim](Press any key to exit...)
68
+ ''', DEFAULT.text_color)
69
+ Cmd.pause_exit(pause=True)
70
+
71
+
72
+
73
+ if __name__ == '__main__':
74
+ help()
xulbux/__init__.py ADDED
@@ -0,0 +1,47 @@
1
+ """
2
+ >>> import XulbuX as xx
3
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4
+ • CUSTOM TYPES:
5
+ • rgba(int,int,int,float)
6
+ • hsla(int,int,int,float)
7
+ • hexa(str)
8
+ • PATH OPERATIONS xx.Path
9
+ • FILE OPERATIONS xx.File
10
+ • JSON FILE OPERATIONS xx.Json
11
+ • SYSTEM ACTIONS xx.System
12
+ • MANAGE ENVIRONMENT VARS xx.EnvVars
13
+ • CMD LOG AND ACTIONS xx.Cmd
14
+ • PRETTY PRINTING xx.FormatCodes
15
+ • COLOR OPERATIONS xx.Color
16
+ • DATA OPERATIONS xx.Data
17
+ • STR OPERATIONS xx.String
18
+ • CODE STRING OPERATIONS xx.Code
19
+ • REGEX PATTERN TEMPLATES xx.Regex
20
+ """
21
+
22
+ __version__ = '1.5.5'
23
+ __author__ = 'XulbuX'
24
+ __email__ = 'xulbux.real@gmail.com'
25
+ __license__ = 'MIT'
26
+ __copyright__ = 'Copyright (c) 2024 XulbuX'
27
+ __url__ = 'https://github.com/XulbuX-dev/Python/tree/main/Libraries/XulbuX'
28
+ __description__ = 'A library which includes a lot of really helpful functions.'
29
+ __all__ = [
30
+ '__help__', '_consts_', 'xx_cmd', 'xx_code', 'xx_color', 'xx_data', 'xx_env_vars', 'xx_file',
31
+ 'xx_format_codes', 'xx_json', 'xx_path', 'xx_regex', 'xx_string', 'xx_system'
32
+ ]
33
+
34
+ from .__help__ import help
35
+ from ._consts_ import *
36
+ from .xx_cmd import *
37
+ from .xx_code import *
38
+ from .xx_color import *
39
+ from .xx_data import *
40
+ from .xx_env_vars import *
41
+ from .xx_file import *
42
+ from .xx_format_codes import *
43
+ from .xx_json import *
44
+ from .xx_path import *
45
+ from .xx_regex import *
46
+ from .xx_string import *
47
+ from .xx_system import *
xulbux/_consts_.py ADDED
@@ -0,0 +1,147 @@
1
+ class DEFAULT:
2
+
3
+ text_color:str = '#95B5FF'
4
+ color:dict[str,str] = {
5
+ 'white': '#F1F2FF',
6
+ 'lightgray': '#B6B7C0',
7
+ 'gray': '#7B7C8D',
8
+ 'darkgray': '#67686C',
9
+ 'black': '#202125',
10
+ 'red': '#FF606A',
11
+ 'coral': '#FF7069',
12
+ 'orange': '#FF876A',
13
+ 'tangerine': '#FF9962',
14
+ 'gold': '#FFAF60',
15
+ 'yellow': '#FFD260',
16
+ 'green': '#7EE787',
17
+ 'teal': '#50EAAF',
18
+ 'cyan': '#3EE6DE',
19
+ 'ice': '#77EFEF',
20
+ 'lightblue': '#60AAFF',
21
+ 'blue': '#8085FF',
22
+ 'lavender': '#9B7DFF',
23
+ 'purple': '#AD68FF',
24
+ 'magenta': '#C860FF',
25
+ 'pink': '#EE60BB',
26
+ 'rose': '#FF6090',
27
+ }
28
+
29
+
30
+
31
+
32
+ class CHARS:
33
+
34
+ # CODE TO SIGNAL, ALL CHARACTERS ARE ALLOWED
35
+ all = '<*allowed>'
36
+
37
+ # DIGIT SETS
38
+ digits = '0123456789'
39
+ float_digits = digits + '.'
40
+ hex_digits = digits + '#abcdefABCDEF'
41
+
42
+ # LETTER CATEGORIES
43
+ lowercase = 'abcdefghijklmnopqrstuvwxyz'
44
+ lowercase_extended = lowercase + 'äëïöüÿàèìòùáéíóúýâêîôûãñõåæç'
45
+ uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
46
+ uppercase_extended = uppercase + 'ÄËÏÖÜÀÈÌÒÙÁÉÍÓÚÝÂÊÎÔÛÃÑÕÅÆÇß'
47
+
48
+ # COMBINED LETTER SETS
49
+ letters = lowercase + uppercase
50
+ letters_extended = lowercase_extended + uppercase_extended
51
+
52
+ # ASCII sets
53
+ special_ascii = ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
54
+ special_ascii_extended = special_ascii + 'ø£Ø×ƒªº¿®¬½¼¡«»░▒▓│┤©╣║╗╝¢¥┐└┴┬├─┼╚╔╩╦╠═╬¤ðÐı┘┌█▄¦▀µþÞ¯´≡­±‗¾¶§÷¸°¨·¹³²■ '
55
+ standard_ascii = special_ascii + digits + letters
56
+ full_ascii = special_ascii_extended + digits + letters_extended
57
+
58
+
59
+
60
+
61
+ class ANSI:
62
+
63
+ global CHAR, START, SEP, END
64
+
65
+ CHAR = char = '\x1b'
66
+ START = start = '['
67
+ SEP = sep = ';'
68
+ END = end = 'm'
69
+
70
+ def seq(parts:int = 1) -> str:
71
+ """Generate an ANSI sequence with `parts` amount of placeholders."""
72
+ return CHAR + START + SEP.join(['{}' for _ in range(parts)]) + END
73
+
74
+ seq_color:str = CHAR + START + '38' + SEP + '2' + SEP + '{}' + SEP + '{}' + SEP + '{}' + END
75
+ seq_bg_color:str = CHAR + START + '48' + SEP + '2' + SEP + '{}' + SEP + '{}' + SEP + '{}' + END
76
+
77
+ color_map:list[str] = [
78
+ ########### DEFAULT CONSOLE COLOR NAMES ############
79
+ 'black',
80
+ 'red',
81
+ 'green',
82
+ 'yellow',
83
+ 'blue',
84
+ 'magenta',
85
+ 'cyan',
86
+ 'white'
87
+ ]
88
+
89
+ codes_map:dict[str|tuple[str], int] = {
90
+ ###################### RESETS ######################
91
+ '_': 0,
92
+ ('_bold','_b'): 22,
93
+ ('_dim','_d'): 22,
94
+ ('_italic','_i'): 23,
95
+ ('_underline','_u'): 24,
96
+ ('_double-underline','_du'): 24,
97
+ ('_inverse','_invert','_in'): 27,
98
+ ('_hidden','_hide','_h'): 28,
99
+ ('_strikethrough','_s'): 29,
100
+ ('_color','_c'): 39,
101
+ ('_background','_bg'): 49,
102
+ ################### TEXT FORMATS ###################
103
+ ('bold','b'): 1,
104
+ ('dim','d'): 2,
105
+ ('italic','i'): 3,
106
+ ('underline','u'): 4,
107
+ ('inverse','invert','in'): 7,
108
+ ('hidden','hide','h'): 8,
109
+ ('strikethrough','s'): 9,
110
+ ('double-underline','du'): 21,
111
+ ############## DEFAULT CONSOLE COLORS ##############
112
+ 'black': 30,
113
+ 'red': 31,
114
+ 'green': 32,
115
+ 'yellow': 33,
116
+ 'blue': 34,
117
+ 'magenta': 35,
118
+ 'cyan': 36,
119
+ 'white': 37,
120
+ ########## BRIGHT DEFAULT CONSOLE COLORS ###########
121
+ ('bright:black','br:black'): 90,
122
+ ('bright:red','br:red'): 91,
123
+ ('bright:green','br:green'): 92,
124
+ ('bright:yellow','br:yellow'): 93,
125
+ ('bright:blue','br:blue'): 94,
126
+ ('bright:magenta','br:magenta'): 95,
127
+ ('bright:cyan','br:cyan'): 96,
128
+ ('bright:white','br:white'): 97,
129
+ ######## DEFAULT CONSOLE BACKGROUND COLORS #########
130
+ 'bg:black': 40,
131
+ 'bg:red': 41,
132
+ 'bg:green': 42,
133
+ 'bg:yellow': 43,
134
+ 'bg:blue': 44,
135
+ 'bg:magenta': 45,
136
+ 'bg:cyan': 46,
137
+ 'bg:white': 47,
138
+ ##### BRIGHT DEFAULT CONSOLE BACKGROUND COLORS #####
139
+ ('bg:bright:black','bg:br:black'): 100,
140
+ ('bg:bright:red','bg:br:red'): 101,
141
+ ('bg:bright:green','bg:br:green'): 102,
142
+ ('bg:bright:yellow','bg:br:yellow'): 103,
143
+ ('bg:bright:blue','bg:br:blue'): 104,
144
+ ('bg:bright:magenta','bg:br:magenta'): 105,
145
+ ('bg:bright:cyan','bg:br:cyan'): 106,
146
+ ('bg:bright:white','bg:br:white'): 107,
147
+ }
xulbux/xx_cmd.py ADDED
@@ -0,0 +1,240 @@
1
+ """
2
+ Functions for logging and other small actions within the console:
3
+ - `Cmd.get_args()`
4
+ - `Cmd.user()`
5
+ - `Cmd.is_admin()`
6
+ - `Cmd.pause_exit()`
7
+ - `Cmd.cls()`
8
+ - `Cmd.log()`
9
+ - `Cmd.debug()`
10
+ - `Cmd.info()`
11
+ - `Cmd.done()`
12
+ - `Cmd.warn()`
13
+ - `Cmd.fail()`
14
+ - `Cmd.exit()`
15
+ - `Cmd.confirm()`
16
+ - `Cmd.restricted_input()`
17
+ - `Cmd.pwd_input()`\n
18
+ ----------------------------------------------------------------------------------------------------------
19
+ You can also use special formatting codes directly inside the log message to change their appearance.<br>
20
+ For more detailed information about formatting codes, see the the `xx_format_codes` description.
21
+ """
22
+
23
+
24
+ from ._consts_ import DEFAULT, CHARS
25
+ from .xx_format_codes import *
26
+ from .xx_string import *
27
+ from .xx_color import *
28
+
29
+ from contextlib import suppress
30
+ import subprocess as _subprocess
31
+ import pyperclip as _pyperclip
32
+ import keyboard as _keyboard
33
+ import getpass as _getpass
34
+ import ctypes as _ctypes
35
+ import shutil as _shutil
36
+ import mouse as _mouse
37
+ import sys as _sys
38
+ import os as _os
39
+
40
+
41
+
42
+
43
+ class Cmd:
44
+
45
+ @staticmethod
46
+ def get_args(find_args:dict) -> dict:
47
+ args = _sys.argv[1:]
48
+ results = {}
49
+ for arg_key, arg_group in find_args.items():
50
+ value = None
51
+ exists = False
52
+ for arg in arg_group:
53
+ if arg in args:
54
+ exists = True
55
+ arg_index = args.index(arg)
56
+ if arg_index + 1 < len(args) and not args[arg_index + 1].startswith('-'):
57
+ value = String.to_type(args[arg_index + 1])
58
+ break
59
+ results[arg_key] = {'exists': exists, 'value': value}
60
+ return results
61
+
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()
66
+
67
+ @staticmethod
68
+ def is_admin() -> bool:
69
+ try:
70
+ if _os.name == 'nt':
71
+ return _ctypes.windll.shell32.IsUserAnAdmin() != 0
72
+ elif _os.name == 'posix':
73
+ return _os.geteuid() == 0
74
+ else:
75
+ return False
76
+ except:
77
+ return False
78
+
79
+ @staticmethod
80
+ def pause_exit(pause:bool = False, exit:bool = False, last_prompt:object = '', exit_code:int = 0, reset_ansi:bool = False) -> None:
81
+ """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)
87
+
88
+ @staticmethod
89
+ def cls() -> None:
90
+ """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)
94
+
95
+ @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:
97
+ """Will print a formatted log message:<br>
98
+ `title` -⠀the title of the log message (e.g. `DEBUG`, `WARN`, `FAIL`, etc.)<br>
99
+ `prompt` -⠀the log message<br>
100
+ `start` -⠀something to print before the log is printed<br>
101
+ `end` -⠀something to print after the log is printed (e.g. `\\n\\n`)<br>
102
+ `title_bg_color` -⠀the background color of the `title`<br>
103
+ `default_color` -⠀the default text color of the `prompt`\n
104
+ --------------------------------------------------------------------------------
105
+ 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)
110
+
111
+ @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:
113
+ """A preset for `log()`: `DEBUG` log message with the options to pause<br>
114
+ at the message and exit the program after the message was printed."""
115
+ if active:
116
+ Cmd.log('DEBUG', prompt, start, end, title_bg_color, default_color)
117
+ Cmd.pause_exit(pause, exit)
118
+
119
+ @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:
121
+ """A preset for `log()`: `INFO` log message with the options to pause<br>
122
+ at the message and exit the program after the message was printed."""
123
+ Cmd.log('INFO', prompt, start, end, title_bg_color, default_color)
124
+ Cmd.pause_exit(pause, exit)
125
+
126
+ @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:
128
+ """A preset for `log()`: `DONE` log message with the options to pause<br>
129
+ at the message and exit the program after the message was printed."""
130
+ Cmd.log('DONE', prompt, start, end, title_bg_color, default_color)
131
+ Cmd.pause_exit(pause, exit)
132
+
133
+ @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:
135
+ """A preset for `log()`: `WARN` log message with the options to pause<br>
136
+ at the message and exit the program after the message was printed."""
137
+ Cmd.log('WARN', prompt, start, end, title_bg_color, default_color)
138
+ Cmd.pause_exit(pause, exit)
139
+
140
+ @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:
142
+ """A preset for `log()`: `FAIL` log message with the options to pause<br>
143
+ at the message and exit the program after the message was printed."""
144
+ Cmd.log('FAIL', prompt, start, end, title_bg_color, default_color)
145
+ Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
146
+
147
+ @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:
149
+ """A preset for `log()`: `EXIT` log message with the options to pause<br>
150
+ at the message and exit the program after the message was printed."""
151
+ Cmd.log('EXIT', prompt, start, end, title_bg_color, default_color)
152
+ Cmd.pause_exit(pause, exit, reset_ansi=reset_ansi)
153
+
154
+ @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:
162
+ """Ask a yes/no question.\n
163
+ -----------------------------------------------------------------------------------
164
+ 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)
168
+ return confirmed
169
+
170
+ @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:
172
+ """Acts like a standard Python `input()` with the advantage, that you can specify:
173
+ - what text characters the user is allowed to type and
174
+ - the minimum and/or maximum length of the users input
175
+ - optional mask character (hide user input, e.g. for passwords)\n
176
+ -----------------------------------------------------------------------------------
177
+ 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:
185
+ 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)
187
+ line_count = len(lines)
188
+ 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')
192
+ 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')
195
+ last_line_count, last_console_width = line_count, console_width
196
+ while True:
197
+ 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())
229
+ 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
234
+ select_all = False
235
+ update_display(Cmd.w())
236
+
237
+ @staticmethod
238
+ def pwd_input(prompt:object = 'Password: ', allowed_chars:str = CHARS.standard_ascii, min_length:int = None, max_length:int = None) -> str:
239
+ """Password input that masks the entered characters with asterisks."""
240
+ return Cmd.restricted_input(prompt, allowed_chars, min_length, max_length, mask_char='*')
xulbux/xx_code.py ADDED
@@ -0,0 +1,102 @@
1
+ from .xx_string import *
2
+ from .xx_regex import *
3
+ from .xx_data import *
4
+
5
+ import regex as _rx
6
+
7
+
8
+
9
+
10
+ class Code:
11
+
12
+ @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:
20
+ """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)
23
+
24
+ @staticmethod
25
+ def get_tab_spaces(code:str) -> int:
26
+ """Will try to get the amount of spaces used for indentation."""
27
+ code_lines = String.get_string_lines(code, remove_empty_lines=True)
28
+ indents = [len(line) - len(line.lstrip()) for line in code_lines]
29
+ non_zero_indents = [i for i in indents if i > 0]
30
+ return min(non_zero_indents) if non_zero_indents else 0
31
+
32
+ @staticmethod
33
+ def change_tab_size(code:str, new_tab_size:int, remove_empty_lines:bool = False) -> str:
34
+ """Replaces all tabs with `new_tab_size` spaces.<br>
35
+ If `remove_empty_lines` is `True`, empty lines will be removed in the process."""
36
+ code_lines = String.get_string_lines(code, remove_empty_lines=True)
37
+ lines = code_lines if remove_empty_lines else String.get_string_lines(code)
38
+ tab_spaces = Code.get_tab_spaces(code)
39
+ if (tab_spaces == new_tab_size) or tab_spaces == 0:
40
+ if remove_empty_lines:
41
+ return '\n'.join(code_lines)
42
+ return code
43
+ result = []
44
+ for line in lines:
45
+ stripped = line.lstrip()
46
+ indent_level = (len(line) - len(stripped)) // tab_spaces
47
+ new_indent = ' ' * (indent_level * new_tab_size)
48
+ result.append(new_indent + stripped)
49
+ return '\n'.join(result)
50
+
51
+ @staticmethod
52
+ def get_func_calls(code:str) -> list:
53
+ """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), []
55
+ for _, func_attrs in funcs:
56
+ nested_calls = _rx.findall(r'(?i)' + Regex.func_call(), func_attrs)
57
+ if nested_calls:
58
+ nested_func_calls.extend(nested_calls)
59
+ return Data.remove_duplicates(funcs + nested_func_calls)
60
+
61
+ @staticmethod
62
+ def is_js(code:str, funcs:list = ['__', '$t', '$lang']) -> bool:
63
+ """Will check if the code is likely to be JavaScript."""
64
+ funcs = '|'.join(funcs)
65
+ js_pattern = _rx.compile(Regex.outside_strings(r'''^(?:
66
+ (\$[\w_]+)\s* # JQUERY-STYLE VARIABLES
67
+ |(\$[\w_]+\s*\() # JQUERY-STYLE FUNCTION CALLS
68
+ |((''' + funcs + r')' + Regex.brackets('()') + r'''\s*) # PREDEFINED FUNCTION CALLS
69
+ |(\bfunction\s*\() # FUNCTION DECLARATIONS
70
+ |(\b(var|let|const)\s+[\w_]+\s*=) # VARIABLE DECLARATIONS
71
+ |(\b(if|for|while|switch)\s*\() # CONTROL STRUCTURES
72
+ |(\b(return|throw)\s+) # RETURN OR THROW STATEMENTS
73
+ |(\bnew\s+[\w_]+\() # OBJECT INSTANTIATION
74
+ |(\b[\w_]+\s*=>\s*{) # ARROW FUNCTIONS
75
+ |(\b(true|false|null|undefined)\b) # JAVASCRIPT LITERALS
76
+ |(\b(document|window|console)\.) # BROWSER OBJECTS
77
+ |(\b[\w_]+\.(forEach|map|filter|reduce)\() # ARRAY METHODS
78
+ |(/[^/\n\r]*?/[gimsuy]*) # REGULAR EXPRESSIONS
79
+ |(===|!==|\+\+|--|\|\||&&) # JAVASCRIPT-SPECIFIC OPERATORS
80
+ |(\bclass\s+[\w_]+) # CLASS DECLARATIONS
81
+ |(\bimport\s+.*?from\s+) # IMPORT STATEMENTS
82
+ |(\bexport\s+(default\s+)?) # EXPORT STATEMENTS
83
+ |(\basync\s+function) # ASYNC FUNCTIONS
84
+ |(\bawait\s+) # AWAIT KEYWORD
85
+ |(\btry\s*{) # TRY-CATCH BLOCKS
86
+ |(\bcatch\s*\()
87
+ |(\bfinally\s*{)
88
+ |(\byield\s+) # GENERATOR FUNCTIONS
89
+ |(\[.*?\]\s*=) # DESTRUCTURING ASSIGNMENT
90
+ |(\.\.\.) # SPREAD OPERATOR
91
+ |(==|!=|>=|<=|>|<) # COMPARISON OPERATORS
92
+ |(\+=|-=|\*=|/=|%=|\*\*=) # COMPOUND ASSIGNMENT OPERATORS
93
+ |(\+|-|\*|/|%|\*\*) # ARITHMETIC OPERATORS
94
+ |(&|\||\^|~|<<|>>|>>>) # BITWISE OPERATORS
95
+ |(\?|:) # TERNARY OPERATOR
96
+ |(\bin\b) # IN OPERATOR
97
+ |(\binstanceof\b) # INSTANCEOF OPERATOR
98
+ |(\bdelete\b) # DELETE OPERATOR
99
+ |(\btypeof\b) # TYPEOF OPERATOR
100
+ |(\bvoid\b) # VOID OPERATOR
101
+ )[\s\S]*$'''), _rx.VERBOSE | _rx.IGNORECASE)
102
+ return bool(js_pattern.fullmatch(code))