xulbux 1.9.5__cp311-cp311-macosx_11_0_arm64.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.
- 455848faf89d8974b22a__mypyc.cpython-311-darwin.so +0 -0
- xulbux/__init__.cpython-311-darwin.so +0 -0
- xulbux/__init__.py +46 -0
- xulbux/base/consts.cpython-311-darwin.so +0 -0
- xulbux/base/consts.py +172 -0
- xulbux/base/decorators.cpython-311-darwin.so +0 -0
- xulbux/base/decorators.py +28 -0
- xulbux/base/exceptions.cpython-311-darwin.so +0 -0
- xulbux/base/exceptions.py +23 -0
- xulbux/base/types.cpython-311-darwin.so +0 -0
- xulbux/base/types.py +118 -0
- xulbux/cli/help.cpython-311-darwin.so +0 -0
- xulbux/cli/help.py +77 -0
- xulbux/code.cpython-311-darwin.so +0 -0
- xulbux/code.py +137 -0
- xulbux/color.cpython-311-darwin.so +0 -0
- xulbux/color.py +1331 -0
- xulbux/console.cpython-311-darwin.so +0 -0
- xulbux/console.py +2069 -0
- xulbux/data.cpython-311-darwin.so +0 -0
- xulbux/data.py +798 -0
- xulbux/env_path.cpython-311-darwin.so +0 -0
- xulbux/env_path.py +123 -0
- xulbux/file.cpython-311-darwin.so +0 -0
- xulbux/file.py +74 -0
- xulbux/file_sys.cpython-311-darwin.so +0 -0
- xulbux/file_sys.py +266 -0
- xulbux/format_codes.cpython-311-darwin.so +0 -0
- xulbux/format_codes.py +722 -0
- xulbux/json.cpython-311-darwin.so +0 -0
- xulbux/json.py +200 -0
- xulbux/regex.cpython-311-darwin.so +0 -0
- xulbux/regex.py +247 -0
- xulbux/string.cpython-311-darwin.so +0 -0
- xulbux/string.py +161 -0
- xulbux/system.cpython-311-darwin.so +0 -0
- xulbux/system.py +313 -0
- xulbux-1.9.5.dist-info/METADATA +271 -0
- xulbux-1.9.5.dist-info/RECORD +43 -0
- xulbux-1.9.5.dist-info/WHEEL +6 -0
- xulbux-1.9.5.dist-info/entry_points.txt +2 -0
- xulbux-1.9.5.dist-info/licenses/LICENSE +21 -0
- xulbux-1.9.5.dist-info/top_level.txt +2 -0
|
Binary file
|
|
Binary file
|
xulbux/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
__package_name__ = "xulbux"
|
|
2
|
+
__version__ = "1.9.5"
|
|
3
|
+
__description__ = "A Python library to simplify common programming tasks."
|
|
4
|
+
__status__ = "Production/Stable"
|
|
5
|
+
|
|
6
|
+
__url__ = "https://github.com/XulbuX/PythonLibraryXulbuX"
|
|
7
|
+
|
|
8
|
+
__author__ = "XulbuX"
|
|
9
|
+
__email__ = "xulbux.real@gmail.com"
|
|
10
|
+
__license__ = "MIT"
|
|
11
|
+
__copyright__ = "Copyright (c) 2024 XulbuX"
|
|
12
|
+
|
|
13
|
+
__requires_python__ = ">=3.10.0"
|
|
14
|
+
__dependencies__ = [
|
|
15
|
+
"keyboard>=0.13.5",
|
|
16
|
+
"prompt_toolkit>=3.0.41",
|
|
17
|
+
"regex>=2023.10.3",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Code",
|
|
22
|
+
"Color",
|
|
23
|
+
"Console",
|
|
24
|
+
"Data",
|
|
25
|
+
"EnvPath",
|
|
26
|
+
"File",
|
|
27
|
+
"FileSys",
|
|
28
|
+
"FormatCodes",
|
|
29
|
+
"Json",
|
|
30
|
+
"Regex",
|
|
31
|
+
"String",
|
|
32
|
+
"System",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
from .code import Code
|
|
36
|
+
from .color import Color
|
|
37
|
+
from .console import Console
|
|
38
|
+
from .data import Data
|
|
39
|
+
from .env_path import EnvPath
|
|
40
|
+
from .file import File
|
|
41
|
+
from .file_sys import FileSys
|
|
42
|
+
from .format_codes import FormatCodes
|
|
43
|
+
from .json import Json
|
|
44
|
+
from .regex import Regex
|
|
45
|
+
from .string import String
|
|
46
|
+
from .system import System
|
|
Binary file
|
xulbux/base/consts.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains constant values used throughout the library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .types import FormattableString, AllTextChars
|
|
6
|
+
|
|
7
|
+
from typing import Final
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class COLOR:
|
|
11
|
+
"""Hexadecimal color presets."""
|
|
12
|
+
|
|
13
|
+
WHITE: Final = "#F1F2FF"
|
|
14
|
+
LIGHT_GRAY: Final = "#B6B7C0"
|
|
15
|
+
GRAY: Final = "#7B7C8D"
|
|
16
|
+
DARK_GRAY: Final = "#67686C"
|
|
17
|
+
BLACK: Final = "#202125"
|
|
18
|
+
RED: Final = "#FF606A"
|
|
19
|
+
CORAL: Final = "#FF7069"
|
|
20
|
+
ORANGE: Final = "#FF876A"
|
|
21
|
+
TANGERINE: Final = "#FF9962"
|
|
22
|
+
GOLD: Final = "#FFAF60"
|
|
23
|
+
YELLOW: Final = "#FFD260"
|
|
24
|
+
LIME: Final = "#C9F16E"
|
|
25
|
+
GREEN: Final = "#7EE787"
|
|
26
|
+
NEON_GREEN: Final = "#4CFF85"
|
|
27
|
+
TEAL: Final = "#50EAAF"
|
|
28
|
+
CYAN: Final = "#3EDEE6"
|
|
29
|
+
ICE: Final = "#77DBEF"
|
|
30
|
+
LIGHT_BLUE: Final = "#60AAFF"
|
|
31
|
+
BLUE: Final = "#8085FF"
|
|
32
|
+
LAVENDER: Final = "#9B7DFF"
|
|
33
|
+
PURPLE: Final = "#AD68FF"
|
|
34
|
+
MAGENTA: Final = "#C860FF"
|
|
35
|
+
PINK: Final = "#F162EF"
|
|
36
|
+
ROSE: Final = "#FF609F"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class CHARS:
|
|
40
|
+
"""Character set constants for text validation and filtering."""
|
|
41
|
+
|
|
42
|
+
ALL: Final = AllTextChars()
|
|
43
|
+
"""Sentinel value indicating all characters are allowed."""
|
|
44
|
+
|
|
45
|
+
DIGITS: Final = "0123456789"
|
|
46
|
+
"""Numeric digits: `0`-`9`"""
|
|
47
|
+
FLOAT_DIGITS: Final = "." + DIGITS
|
|
48
|
+
"""Numeric digits with decimal point: `0`-`9` and `.`"""
|
|
49
|
+
HEX_DIGITS: Final = "#" + DIGITS + "abcdefABCDEF"
|
|
50
|
+
"""Hexadecimal digits: `0`-`9`, `a`-`f`, `A`-`F`, and `#`"""
|
|
51
|
+
|
|
52
|
+
LOWERCASE: Final = "abcdefghijklmnopqrstuvwxyz"
|
|
53
|
+
"""Lowercase ASCII letters: `a`-`z`"""
|
|
54
|
+
LOWERCASE_EXTENDED: Final = LOWERCASE + "äëïöüÿàèìòùáéíóúýâêîôûãñõåæç"
|
|
55
|
+
"""Lowercase ASCII letters with diacritic marks."""
|
|
56
|
+
UPPERCASE: Final = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
57
|
+
"""Uppercase ASCII letters: `A`-`Z`"""
|
|
58
|
+
UPPERCASE_EXTENDED: Final = UPPERCASE + "ÄËÏÖÜÀÈÌÒÙÁÉÍÓÚÝÂÊÎÔÛÃÑÕÅÆÇß"
|
|
59
|
+
"""Uppercase ASCII letters with diacritic marks."""
|
|
60
|
+
|
|
61
|
+
LETTERS: Final = LOWERCASE + UPPERCASE
|
|
62
|
+
"""All ASCII letters: `a`-`z` and `A`-`Z`"""
|
|
63
|
+
LETTERS_EXTENDED: Final = LOWERCASE_EXTENDED + UPPERCASE_EXTENDED
|
|
64
|
+
"""All ASCII letters with diacritic marks."""
|
|
65
|
+
|
|
66
|
+
SPECIAL_ASCII: Final = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
|
67
|
+
"""Standard ASCII special characters and symbols."""
|
|
68
|
+
SPECIAL_ASCII_EXTENDED: Final = SPECIAL_ASCII + "ø£Ø×ƒªº¿®¬½¼¡«»░▒▓│┤©╣║╗╝¢¥┐└┴┬├─┼╚╔╩╦╠═╬¤ðÐı┘┌█▄¦▀µþÞ¯´≡±‗¾¶§÷¸°¨·¹³²■ "
|
|
69
|
+
"""Standard and extended ASCII special characters."""
|
|
70
|
+
STANDARD_ASCII: Final = DIGITS + LETTERS + SPECIAL_ASCII
|
|
71
|
+
"""All standard ASCII characters (letters, digits, and symbols)."""
|
|
72
|
+
FULL_ASCII: Final = DIGITS + LETTERS_EXTENDED + SPECIAL_ASCII_EXTENDED
|
|
73
|
+
"""Complete ASCII character set including extended characters."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ANSI:
|
|
77
|
+
"""Constants and utilities for ANSI escape code sequences."""
|
|
78
|
+
|
|
79
|
+
CHAR_ESCAPED: Final = r"\x1b"
|
|
80
|
+
"""Printable ANSI escape character."""
|
|
81
|
+
CHAR: Final = "\x1b"
|
|
82
|
+
"""ANSI escape character."""
|
|
83
|
+
START: Final = "["
|
|
84
|
+
"""Start of an ANSI escape sequence."""
|
|
85
|
+
SEP: Final = ";"
|
|
86
|
+
"""Separator between ANSI escape sequence parts."""
|
|
87
|
+
END: Final = "m"
|
|
88
|
+
"""End of an ANSI escape sequence."""
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def seq(cls, placeholders: int = 1) -> FormattableString:
|
|
92
|
+
"""Generates an ANSI escape sequence with the specified number of placeholders."""
|
|
93
|
+
return cls.CHAR + cls.START + cls.SEP.join(["{}" for _ in range(placeholders)]) + cls.END
|
|
94
|
+
|
|
95
|
+
SEQ_COLOR: Final[FormattableString] = CHAR + START + "38" + SEP + "2" + SEP + "{}" + SEP + "{}" + SEP + "{}" + END
|
|
96
|
+
"""ANSI escape sequence with three placeholders for setting the RGB text color."""
|
|
97
|
+
SEQ_BG_COLOR: Final[FormattableString] = CHAR + START + "48" + SEP + "2" + SEP + "{}" + SEP + "{}" + SEP + "{}" + END
|
|
98
|
+
"""ANSI escape sequence with three placeholders for setting the RGB background color."""
|
|
99
|
+
|
|
100
|
+
COLOR_MAP: Final[tuple[str, ...]] = (
|
|
101
|
+
########### DEFAULT CONSOLE COLOR NAMES ############
|
|
102
|
+
"black",
|
|
103
|
+
"red",
|
|
104
|
+
"green",
|
|
105
|
+
"yellow",
|
|
106
|
+
"blue",
|
|
107
|
+
"magenta",
|
|
108
|
+
"cyan",
|
|
109
|
+
"white",
|
|
110
|
+
)
|
|
111
|
+
"""The standard terminal color names."""
|
|
112
|
+
|
|
113
|
+
CODES_MAP: Final[dict[str | tuple[str, ...], int]] = {
|
|
114
|
+
################# SPECIFIC RESETS ##################
|
|
115
|
+
"_": 0,
|
|
116
|
+
("_bold", "_b"): 22,
|
|
117
|
+
("_dim", "_d"): 22,
|
|
118
|
+
("_italic", "_i"): 23,
|
|
119
|
+
("_underline", "_u"): 24,
|
|
120
|
+
("_double-underline", "_du"): 24,
|
|
121
|
+
("_inverse", "_invert", "_in"): 27,
|
|
122
|
+
("_hidden", "_hide", "_h"): 28,
|
|
123
|
+
("_strikethrough", "_s"): 29,
|
|
124
|
+
("_color", "_c"): 39,
|
|
125
|
+
("_background", "_bg"): 49,
|
|
126
|
+
################### TEXT STYLES ####################
|
|
127
|
+
("bold", "b"): 1,
|
|
128
|
+
("dim", "d"): 2,
|
|
129
|
+
("italic", "i"): 3,
|
|
130
|
+
("underline", "u"): 4,
|
|
131
|
+
("inverse", "invert", "in"): 7,
|
|
132
|
+
("hidden", "hide", "h"): 8,
|
|
133
|
+
("strikethrough", "s"): 9,
|
|
134
|
+
("double-underline", "du"): 21,
|
|
135
|
+
################## DEFAULT COLORS ##################
|
|
136
|
+
"black": 30,
|
|
137
|
+
"red": 31,
|
|
138
|
+
"green": 32,
|
|
139
|
+
"yellow": 33,
|
|
140
|
+
"blue": 34,
|
|
141
|
+
"magenta": 35,
|
|
142
|
+
"cyan": 36,
|
|
143
|
+
"white": 37,
|
|
144
|
+
############## BRIGHT DEFAULT COLORS ###############
|
|
145
|
+
"br:black": 90,
|
|
146
|
+
"br:red": 91,
|
|
147
|
+
"br:green": 92,
|
|
148
|
+
"br:yellow": 93,
|
|
149
|
+
"br:blue": 94,
|
|
150
|
+
"br:magenta": 95,
|
|
151
|
+
"br:cyan": 96,
|
|
152
|
+
"br:white": 97,
|
|
153
|
+
############ DEFAULT BACKGROUND COLORS #############
|
|
154
|
+
"bg:black": 40,
|
|
155
|
+
"bg:red": 41,
|
|
156
|
+
"bg:green": 42,
|
|
157
|
+
"bg:yellow": 43,
|
|
158
|
+
"bg:blue": 44,
|
|
159
|
+
"bg:magenta": 45,
|
|
160
|
+
"bg:cyan": 46,
|
|
161
|
+
"bg:white": 47,
|
|
162
|
+
######### BRIGHT DEFAULT BACKGROUND COLORS #########
|
|
163
|
+
"bg:br:black": 100,
|
|
164
|
+
"bg:br:red": 101,
|
|
165
|
+
"bg:br:green": 102,
|
|
166
|
+
"bg:br:yellow": 103,
|
|
167
|
+
"bg:br:blue": 104,
|
|
168
|
+
"bg:br:magenta": 105,
|
|
169
|
+
"bg:br:cyan": 106,
|
|
170
|
+
"bg:br:white": 107,
|
|
171
|
+
}
|
|
172
|
+
"""Dictionary mapping format keys to their corresponding ANSI code numbers."""
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains custom decorators used throughout the library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Callable, TypeVar, Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _noop_decorator(obj: T) -> T:
|
|
12
|
+
"""No-op decorator that returns the object unchanged."""
|
|
13
|
+
return obj
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def mypyc_attr(**kwargs: Any) -> Callable[[T], T]:
|
|
17
|
+
"""A custom decorator that wraps `mypy_extensions.mypyc_attr` when available,<br>
|
|
18
|
+
or acts as a no-op decorator when `mypy_extensions` is not installed.\n
|
|
19
|
+
This allows the use of `mypyc` compilation hints for compiling without making
|
|
20
|
+
`mypy_extensions` a required dependency.\n
|
|
21
|
+
-----------------------------------------------------------------------------------------
|
|
22
|
+
- `**kwargs` -⠀keyword arguments to pass to `mypy_extensions.mypyc_attr` if available"""
|
|
23
|
+
try:
|
|
24
|
+
from mypy_extensions import mypyc_attr as _mypyc_attr
|
|
25
|
+
return _mypyc_attr(**kwargs)
|
|
26
|
+
except ImportError:
|
|
27
|
+
# IF 'mypy_extensions' IS NOT INSTALLED, JUST RETURN A NO-OP DECORATOR
|
|
28
|
+
return _noop_decorator
|
|
Binary file
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains all custom exception classes used throughout the library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .decorators import mypyc_attr
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
################################################## FILE ##################################################
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@mypyc_attr(native_class=False)
|
|
12
|
+
class SameContentFileExistsError(FileExistsError):
|
|
13
|
+
"""Raised when attempting to create a file that already exists with identical content."""
|
|
14
|
+
...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
################################################## PATH ##################################################
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@mypyc_attr(native_class=False)
|
|
21
|
+
class PathNotFoundError(FileNotFoundError):
|
|
22
|
+
"""Raised when a file system path does not exist or cannot be accessed."""
|
|
23
|
+
...
|
|
Binary file
|
xulbux/base/types.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains all custom type definitions used throughout the library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Annotated, TypeAlias, TypedDict, Optional, Protocol, Literal, Union, Any
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# PREVENT CIRCULAR IMPORTS
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ..color import rgba, hsla, hexa
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
################################################## Annotated ##################################################
|
|
14
|
+
|
|
15
|
+
Int_0_100 = Annotated[int, "Integer constrained to the range [0, 100] inclusive."]
|
|
16
|
+
"""Integer constrained to the range [0, 100] inclusive."""
|
|
17
|
+
Int_0_255 = Annotated[int, "Integer constrained to the range [0, 255] inclusive."]
|
|
18
|
+
"""Integer constrained to the range [0, 255] inclusive."""
|
|
19
|
+
Int_0_360 = Annotated[int, "Integer constrained to the range [0, 360] inclusive."]
|
|
20
|
+
"""Integer constrained to the range [0, 360] inclusive."""
|
|
21
|
+
Float_0_1 = Annotated[float, "Float constrained to the range [0.0, 1.0] inclusive."]
|
|
22
|
+
"""Float constrained to the range [0.0, 1.0] inclusive."""
|
|
23
|
+
|
|
24
|
+
FormattableString = Annotated[str, "String made to be formatted with the `.format()` method."]
|
|
25
|
+
"""String made to be formatted with the `.format()` method."""
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
################################################## TypeAlias ##################################################
|
|
29
|
+
|
|
30
|
+
PathsList: TypeAlias = Union[list[Path], list[str], list[Path | str]]
|
|
31
|
+
"""Union of all supported list types for a list of paths."""
|
|
32
|
+
|
|
33
|
+
DataStructure: TypeAlias = Union[list, tuple, set, frozenset, dict]
|
|
34
|
+
"""Union of supported data structures used in the `data` module."""
|
|
35
|
+
DataStructureTypes = (list, tuple, set, frozenset, dict)
|
|
36
|
+
"""Tuple of supported data structures used in the `data` module."""
|
|
37
|
+
|
|
38
|
+
IndexIterable: TypeAlias = Union[list, tuple, set, frozenset]
|
|
39
|
+
"""Union of all iterable types that support indexing operations."""
|
|
40
|
+
IndexIterableTypes = (list, tuple, set, frozenset)
|
|
41
|
+
"""Tuple of all iterable types that support indexing operations."""
|
|
42
|
+
|
|
43
|
+
Rgba: TypeAlias = Union[
|
|
44
|
+
tuple[Int_0_255, Int_0_255, Int_0_255],
|
|
45
|
+
tuple[Int_0_255, Int_0_255, Int_0_255, Float_0_1],
|
|
46
|
+
list[Int_0_255],
|
|
47
|
+
list[Union[Int_0_255, Float_0_1]],
|
|
48
|
+
dict[str, Union[int, float]],
|
|
49
|
+
"rgba",
|
|
50
|
+
str,
|
|
51
|
+
]
|
|
52
|
+
"""Matches all supported RGBA color value formats."""
|
|
53
|
+
Hsla: TypeAlias = Union[
|
|
54
|
+
tuple[Int_0_360, Int_0_100, Int_0_100],
|
|
55
|
+
tuple[Int_0_360, Int_0_100, Int_0_100, Float_0_1],
|
|
56
|
+
list[Union[Int_0_360, Int_0_100]],
|
|
57
|
+
list[Union[Int_0_360, Int_0_100, Float_0_1]],
|
|
58
|
+
dict[str, Union[int, float]],
|
|
59
|
+
"hsla",
|
|
60
|
+
str,
|
|
61
|
+
]
|
|
62
|
+
"""Matches all supported HSLA color value formats."""
|
|
63
|
+
Hexa: TypeAlias = Union[str, int, "hexa"]
|
|
64
|
+
"""Matches all supported hexadecimal color value formats."""
|
|
65
|
+
|
|
66
|
+
AnyRgba: TypeAlias = Any
|
|
67
|
+
"""Generic type alias for RGBA color values in any supported format (type checking disabled)."""
|
|
68
|
+
AnyHsla: TypeAlias = Any
|
|
69
|
+
"""Generic type alias for HSLA color values in any supported format (type checking disabled)."""
|
|
70
|
+
AnyHexa: TypeAlias = Any
|
|
71
|
+
"""Generic type alias for hexadecimal color values in any supported format (type checking disabled)."""
|
|
72
|
+
|
|
73
|
+
ArgParseConfig: TypeAlias = Union[set[str], "ArgConfigWithDefault", Literal["before", "after"]]
|
|
74
|
+
"""Matches the command-line-parsing configuration of a single argument."""
|
|
75
|
+
ArgParseConfigs: TypeAlias = dict[str, ArgParseConfig]
|
|
76
|
+
"""Matches the command-line-parsing configurations of multiple arguments, packed in a dictionary."""
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
################################################## Sentinel ##################################################
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class AllTextChars:
|
|
83
|
+
"""Sentinel class indicating all characters are allowed."""
|
|
84
|
+
...
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
################################################## TypedDict ##################################################
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ArgConfigWithDefault(TypedDict):
|
|
91
|
+
"""Configuration schema for a flagged command-line argument that has a specified default value."""
|
|
92
|
+
flags: set[str]
|
|
93
|
+
default: str
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ArgData(TypedDict):
|
|
97
|
+
"""Schema for the resulting data of parsing a single command-line argument."""
|
|
98
|
+
exists: bool
|
|
99
|
+
is_pos: bool
|
|
100
|
+
values: list[str]
|
|
101
|
+
flag: Optional[str]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class MissingLibsMsgs(TypedDict):
|
|
105
|
+
"""Configuration schema for custom messages in `System.check_libs()` when checking library dependencies."""
|
|
106
|
+
found_missing: str
|
|
107
|
+
should_install: str
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
################################################## Protocol ##################################################
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ProgressUpdater(Protocol):
|
|
114
|
+
"""Protocol for a progress updater function used in console progress bars."""
|
|
115
|
+
|
|
116
|
+
def __call__(self, current: Optional[int] = None, label: Optional[str] = None) -> None:
|
|
117
|
+
"""Update the current progress value and/or label."""
|
|
118
|
+
...
|
|
Binary file
|
xulbux/cli/help.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from .. import __version__
|
|
2
|
+
from ..format_codes import FormatCodes
|
|
3
|
+
from ..console import Console
|
|
4
|
+
|
|
5
|
+
from urllib.error import HTTPError
|
|
6
|
+
from typing import Optional
|
|
7
|
+
import urllib.request as _request
|
|
8
|
+
import json as _json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_latest_version() -> Optional[str]:
|
|
12
|
+
with _request.urlopen(URL) as response:
|
|
13
|
+
if response.status == 200:
|
|
14
|
+
data = _json.load(response)
|
|
15
|
+
return data["info"]["version"]
|
|
16
|
+
else:
|
|
17
|
+
raise HTTPError(URL, response.status, "Failed to fetch latest version info", response.headers, None)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_latest_version() -> Optional[bool]:
|
|
21
|
+
try:
|
|
22
|
+
if (latest := get_latest_version()) in {"", None}:
|
|
23
|
+
return None
|
|
24
|
+
latest_v_parts = tuple(int(part) for part in (latest or "").lower().lstrip("v").split("."))
|
|
25
|
+
installed_v_parts = tuple(int(part) for part in __version__.lower().lstrip("v").split("."))
|
|
26
|
+
return latest_v_parts <= installed_v_parts
|
|
27
|
+
except Exception:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
URL = "https://pypi.org/pypi/xulbux/json"
|
|
32
|
+
IS_LATEST_VERSION = is_latest_version()
|
|
33
|
+
|
|
34
|
+
CLI_COLORS = {
|
|
35
|
+
"border": "dim|br:black",
|
|
36
|
+
"class": "br:cyan",
|
|
37
|
+
"const": "br:blue",
|
|
38
|
+
"func": "br:green",
|
|
39
|
+
"heading": "br:white",
|
|
40
|
+
"import": "magenta",
|
|
41
|
+
"lib": "br:magenta",
|
|
42
|
+
"link": "u|br:blue",
|
|
43
|
+
"notice": "br:yellow",
|
|
44
|
+
"punctuator": "br:black",
|
|
45
|
+
"text": "white",
|
|
46
|
+
}
|
|
47
|
+
CLI_HELP = FormatCodes.to_ansi(
|
|
48
|
+
rf""" [_|b|#7075FF] __ __
|
|
49
|
+
[b|#7075FF] _ __ __ __/ / / /_ __ ___ __
|
|
50
|
+
[b|#7075FF] | |/ // / / / / / __ \/ / / | |/ /
|
|
51
|
+
[b|#7075FF] > , </ /_/ / /_/ /_/ / /_/ /> , <
|
|
52
|
+
[b|#7075FF]/_/|_|\____/\__/\____/\____//_/|_| [*|#000|BG:#8085FF] v[b]{__version__} [*|dim|{CLI_COLORS["notice"]}]({"" if IS_LATEST_VERSION else " (newer available)"})[*]
|
|
53
|
+
|
|
54
|
+
[i|#9095FF]A TON OF COOL FUNCTIONS, YOU NEED![*]
|
|
55
|
+
|
|
56
|
+
[b|{CLI_COLORS["heading"]}](Usage:)[*]
|
|
57
|
+
[{CLI_COLORS["border"]}](╭────────────────────────────────────────────────────╮)[*]
|
|
58
|
+
[{CLI_COLORS["border"]}](│) [i|{CLI_COLORS["punctuator"]}](# LIBRARY CONSTANTS)[*] [{CLI_COLORS["border"]}](│)[*]
|
|
59
|
+
[{CLI_COLORS["border"]}](│) [{CLI_COLORS["import"]}]from [{CLI_COLORS["lib"]}]xulbux[{CLI_COLORS["punctuator"]}].[{CLI_COLORS["lib"]}]base[{CLI_COLORS["punctuator"]}].[{CLI_COLORS["lib"]}]consts [{CLI_COLORS["import"]}]import [{CLI_COLORS["const"]}]COLOR[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["const"]}]CHARS[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["const"]}]ANSI[*] [{CLI_COLORS["border"]}](│)[*]
|
|
60
|
+
[{CLI_COLORS["border"]}](│) [i|{CLI_COLORS["punctuator"]}](# Main Classes)[*] [{CLI_COLORS["border"]}](│)[*]
|
|
61
|
+
[{CLI_COLORS["border"]}](│) [{CLI_COLORS["import"]}]from [{CLI_COLORS["lib"]}]xulbux [{CLI_COLORS["import"]}]import [{CLI_COLORS["class"]}]Code[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["class"]}]Color[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["class"]}]Console[{CLI_COLORS["punctuator"]}], ...[*] [{CLI_COLORS["border"]}](│)[*]
|
|
62
|
+
[{CLI_COLORS["border"]}](│) [i|{CLI_COLORS["punctuator"]}](# module specific imports)[*] [{CLI_COLORS["border"]}](│)[*]
|
|
63
|
+
[{CLI_COLORS["border"]}](│) [{CLI_COLORS["import"]}]from [{CLI_COLORS["lib"]}]xulbux[{CLI_COLORS["punctuator"]}].[{CLI_COLORS["lib"]}]color [{CLI_COLORS["import"]}]import [{CLI_COLORS["func"]}]rgba[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["func"]}]hsla[{CLI_COLORS["punctuator"]}], [{CLI_COLORS["func"]}]hexa[*] [{CLI_COLORS["border"]}](│)
|
|
64
|
+
[{CLI_COLORS["border"]}](╰────────────────────────────────────────────────────╯)[*]
|
|
65
|
+
[b|{CLI_COLORS["heading"]}](Documentation:)[*]
|
|
66
|
+
[{CLI_COLORS["border"]}](╭────────────────────────────────────────────────────╮)[*]
|
|
67
|
+
[{CLI_COLORS["border"]}](│) [{CLI_COLORS["text"]}]For more information see the GitHub page. [{CLI_COLORS["border"]}](│)[*]
|
|
68
|
+
[{CLI_COLORS["border"]}](│) [{CLI_COLORS["link"]}](https://github.com/XulbuX/PythonLibraryXulbuX/wiki) [{CLI_COLORS["border"]}](│)[*]
|
|
69
|
+
[{CLI_COLORS["border"]}](╰────────────────────────────────────────────────────╯)[*]
|
|
70
|
+
[_]"""
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def show_help() -> None:
|
|
75
|
+
FormatCodes._config_console()
|
|
76
|
+
print(CLI_HELP)
|
|
77
|
+
Console.pause_exit(pause=True, prompt=" [dim](Press any key to exit...)\n\n")
|
|
Binary file
|
xulbux/code.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides the `Code` class, which offers methods to work with code strings.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .string import String
|
|
6
|
+
from .regex import Regex
|
|
7
|
+
from .data import Data
|
|
8
|
+
|
|
9
|
+
import regex as _rx
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Code:
|
|
13
|
+
"""This class includes methods to work with code strings."""
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def add_indent(cls, code: str, indent: int) -> str:
|
|
17
|
+
"""Adds `indent` spaces at the beginning of each line.\n
|
|
18
|
+
--------------------------------------------------------------------------
|
|
19
|
+
- `code` -⠀the code to indent
|
|
20
|
+
- `indent` -⠀the amount of spaces to add at the beginning of each line"""
|
|
21
|
+
if indent < 0:
|
|
22
|
+
raise ValueError(f"The 'indent' parameter must be non-negative, got {indent!r}")
|
|
23
|
+
|
|
24
|
+
return "\n".join(" " * indent + line for line in code.splitlines())
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def get_tab_spaces(cls, code: str) -> int:
|
|
28
|
+
"""Will try to get the amount of spaces used for indentation.\n
|
|
29
|
+
----------------------------------------------------------------
|
|
30
|
+
- `code` -⠀the code to analyze"""
|
|
31
|
+
indents = [len(line) - len(line.lstrip()) for line in String.get_lines(code, remove_empty_lines=True)]
|
|
32
|
+
return min(non_zero_indents) if (non_zero_indents := [i for i in indents if i > 0]) else 0
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def change_tab_size(cls, code: str, new_tab_size: int, remove_empty_lines: bool = False) -> str:
|
|
36
|
+
"""Replaces all tabs with `new_tab_size` spaces.\n
|
|
37
|
+
--------------------------------------------------------------------------------
|
|
38
|
+
- `code` -⠀the code to modify the tab size of
|
|
39
|
+
- `new_tab_size` -⠀the new amount of spaces per tab
|
|
40
|
+
- `remove_empty_lines` -⠀is true, empty lines will be removed in the process"""
|
|
41
|
+
if new_tab_size < 0:
|
|
42
|
+
raise ValueError(f"The 'new_tab_size' parameter must be non-negative, got {new_tab_size!r}")
|
|
43
|
+
|
|
44
|
+
code_lines = String.get_lines(code, remove_empty_lines=remove_empty_lines)
|
|
45
|
+
|
|
46
|
+
if ((tab_spaces := cls.get_tab_spaces(code)) == new_tab_size) or tab_spaces == 0:
|
|
47
|
+
if remove_empty_lines:
|
|
48
|
+
return "\n".join(code_lines)
|
|
49
|
+
return code
|
|
50
|
+
|
|
51
|
+
result = []
|
|
52
|
+
for line in code_lines:
|
|
53
|
+
indent_level = (len(line) - len(stripped := line.lstrip())) // tab_spaces
|
|
54
|
+
result.append((" " * (indent_level * new_tab_size)) + stripped)
|
|
55
|
+
|
|
56
|
+
return "\n".join(result)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def get_func_calls(cls, code: str) -> list:
|
|
60
|
+
"""Will try to get all function calls and return them as a list.\n
|
|
61
|
+
-------------------------------------------------------------------
|
|
62
|
+
- `code` -⠀the code to analyze"""
|
|
63
|
+
nested_func_calls = []
|
|
64
|
+
|
|
65
|
+
for _, func_attrs in (funcs := _rx.findall(r"(?i)" + Regex.func_call(), code)):
|
|
66
|
+
if (nested_calls := _rx.findall(r"(?i)" + Regex.func_call(), func_attrs)):
|
|
67
|
+
nested_func_calls.extend(nested_calls)
|
|
68
|
+
|
|
69
|
+
return list(Data.remove_duplicates(funcs + nested_func_calls))
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def is_js(cls, code: str, funcs: set[str] = {"__", "$t", "$lang"}) -> bool:
|
|
73
|
+
"""Will check if the code is very likely to be JavaScript.\n
|
|
74
|
+
-------------------------------------------------------------
|
|
75
|
+
- `code` -⠀the code to analyze
|
|
76
|
+
- `funcs` -⠀a list of custom function names to check for"""
|
|
77
|
+
if len(code.strip()) < 3:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
for func in funcs:
|
|
81
|
+
if _rx.match(r"^[\s\n]*" + _rx.escape(func) + r"\([^\)]*\)[\s\n]*$", code):
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
direct_js_patterns = [
|
|
85
|
+
r"""^[\s\n]*\$\(["'][^"']+["']\)\.[\w]+\([^\)]*\);?[\s\n]*$""", # jQuery calls
|
|
86
|
+
r"^[\s\n]*\$\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # $.ajax(), etc.
|
|
87
|
+
r"^[\s\n]*\(\s*function\s*\(\)\s*\{.*\}\s*\)\(\);?[\s\n]*$", # IIFE
|
|
88
|
+
r"^[\s\n]*document\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # document.getElementById()
|
|
89
|
+
r"^[\s\n]*window\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # window.alert()
|
|
90
|
+
r"^[\s\n]*console\.[a-zA-Z]\w*\([^\)]*\);?[\s\n]*$", # console.log()
|
|
91
|
+
]
|
|
92
|
+
for pattern in direct_js_patterns:
|
|
93
|
+
if _rx.match(pattern, code):
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
arrow_function_patterns = [
|
|
97
|
+
r"^[\s\n]*\b[\w_]+\s*=\s*\([^\)]*\)\s*=>\s*[^;{]*[;]?[\s\n]*$", # const x = (y) => y*2;
|
|
98
|
+
r"^[\s\n]*\b[\w_]+\s*=\s*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$", # const x = y => y*2;
|
|
99
|
+
r"^[\s\n]*\(\s*[\w_,\s]+\s*\)\s*=>\s*[^;{]*[;]?[\s\n]*$", # (x) => x*2
|
|
100
|
+
r"^[\s\n]*[\w_]+\s*=>\s*[^;{]*[;]?[\s\n]*$", # x => x*2
|
|
101
|
+
]
|
|
102
|
+
for pattern in arrow_function_patterns:
|
|
103
|
+
if _rx.match(pattern, code):
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
js_score = 0.0
|
|
107
|
+
funcs_pattern = r"(" + "|".join(_rx.escape(func) for func in funcs) + r")" + Regex.brackets("()")
|
|
108
|
+
js_indicators: list[tuple[str, float]] = [
|
|
109
|
+
(r"\b(var|let|const)\s+[\w_$]+", 2.0), # JS VARIABLE DECLARATIONS
|
|
110
|
+
(r"\$[\w_$]+\s*=", 2.0), # jQuery-STYLE VARIABLES
|
|
111
|
+
(r"\$[\w_$]+\s*\(", 2.0), # jQuery FUNCTION CALLS
|
|
112
|
+
(r"\bfunction\s*[\w_$]*\s*\(", 2.0), # FUNCTION DECLARATIONS
|
|
113
|
+
(r"[\w_$]+\s*=\s*function\s*\(", 2.0), # FUNCTION ASSIGNMENTS
|
|
114
|
+
(r"\b[\w_$]+\s*=>\s*[\{\(]", 2.0), # ARROW FUNCTIONS
|
|
115
|
+
(r"\(function\s*\(\)\s*\{", 2.0), # IIFE PATTERN
|
|
116
|
+
(funcs_pattern, 2.0), # CUSTOM PREDEFINED FUNCTIONS
|
|
117
|
+
(r"\b(true|false|null|undefined)\b", 1.0), # JS LITERALS
|
|
118
|
+
(r"===|!==|\+\+|--|\|\||&&", 1.5), # JS-SPECIFIC OPERATORS
|
|
119
|
+
(r"\bnew\s+[\w_$]+\s*\(", 1.5), # OBJECT INSTANTIATION WITH NEW
|
|
120
|
+
(r"\b(document|window|console|Math|Array|Object|String|Number)\.", 2.0), # JS OBJECTS
|
|
121
|
+
(r"\basync\s+function|\bawait\b", 2.0), # ASYNC/AWAIT
|
|
122
|
+
(r"\b(if|for|while|switch)\s*\([^)]*\)\s*\{", 1.0), # CONTROL STRUCTURES WITH BRACES
|
|
123
|
+
(r"\btry\s*\{[^}]*\}\s*catch\s*\(", 1.5), # TRY-CATCH
|
|
124
|
+
(r";[\s\n]*$", 0.5), # SEMICOLON LINE ENDINGS
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
line_endings = [line.strip() for line in code.splitlines() if line.strip()]
|
|
128
|
+
if (semicolon_endings := sum(1 for line in line_endings if line.endswith(";"))) >= 1:
|
|
129
|
+
js_score += min(semicolon_endings, 2)
|
|
130
|
+
if (opening_braces := code.count("{")) > 0 and opening_braces == code.count("}"):
|
|
131
|
+
js_score += 1
|
|
132
|
+
|
|
133
|
+
for pattern, score in js_indicators:
|
|
134
|
+
if (matches := _rx.compile(pattern, _rx.IGNORECASE).findall(code)):
|
|
135
|
+
js_score += len(matches) * score
|
|
136
|
+
|
|
137
|
+
return js_score >= 2.0
|
|
Binary file
|