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.
Files changed (43) hide show
  1. 455848faf89d8974b22a__mypyc.cpython-311-darwin.so +0 -0
  2. xulbux/__init__.cpython-311-darwin.so +0 -0
  3. xulbux/__init__.py +46 -0
  4. xulbux/base/consts.cpython-311-darwin.so +0 -0
  5. xulbux/base/consts.py +172 -0
  6. xulbux/base/decorators.cpython-311-darwin.so +0 -0
  7. xulbux/base/decorators.py +28 -0
  8. xulbux/base/exceptions.cpython-311-darwin.so +0 -0
  9. xulbux/base/exceptions.py +23 -0
  10. xulbux/base/types.cpython-311-darwin.so +0 -0
  11. xulbux/base/types.py +118 -0
  12. xulbux/cli/help.cpython-311-darwin.so +0 -0
  13. xulbux/cli/help.py +77 -0
  14. xulbux/code.cpython-311-darwin.so +0 -0
  15. xulbux/code.py +137 -0
  16. xulbux/color.cpython-311-darwin.so +0 -0
  17. xulbux/color.py +1331 -0
  18. xulbux/console.cpython-311-darwin.so +0 -0
  19. xulbux/console.py +2069 -0
  20. xulbux/data.cpython-311-darwin.so +0 -0
  21. xulbux/data.py +798 -0
  22. xulbux/env_path.cpython-311-darwin.so +0 -0
  23. xulbux/env_path.py +123 -0
  24. xulbux/file.cpython-311-darwin.so +0 -0
  25. xulbux/file.py +74 -0
  26. xulbux/file_sys.cpython-311-darwin.so +0 -0
  27. xulbux/file_sys.py +266 -0
  28. xulbux/format_codes.cpython-311-darwin.so +0 -0
  29. xulbux/format_codes.py +722 -0
  30. xulbux/json.cpython-311-darwin.so +0 -0
  31. xulbux/json.py +200 -0
  32. xulbux/regex.cpython-311-darwin.so +0 -0
  33. xulbux/regex.py +247 -0
  34. xulbux/string.cpython-311-darwin.so +0 -0
  35. xulbux/string.py +161 -0
  36. xulbux/system.cpython-311-darwin.so +0 -0
  37. xulbux/system.py +313 -0
  38. xulbux-1.9.5.dist-info/METADATA +271 -0
  39. xulbux-1.9.5.dist-info/RECORD +43 -0
  40. xulbux-1.9.5.dist-info/WHEEL +6 -0
  41. xulbux-1.9.5.dist-info/entry_points.txt +2 -0
  42. xulbux-1.9.5.dist-info/licenses/LICENSE +21 -0
  43. xulbux-1.9.5.dist-info/top_level.txt +2 -0
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."""
@@ -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
@@ -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