Skip to content

Commit 17809d1

Browse files
committed
remove unnecessary runtime type-checks
1 parent 6852fd0 commit 17809d1

13 files changed

Lines changed: 105 additions & 695 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
## ... `v1.9.2`
2121
* Added a new class `LazyRegex` to the `regex` module, which is used to define regex patterns that are only compiled when they are used for the first time.
2222
* Removed unnecessary character escaping in the precompiled regex patterns in the `console` module.
23+
* Removed all the runtime type-checks that can also be checked using static type-checking tools, since you're supposed to use type checkers in modern python anyway, and to improve performance.
2324

2425
**BREAKING CHANGES:**
2526
* Replaced the internal `_COMPILED` regex pattern dictionaries with `LazyRegex` objects so it won't compile all regex patterns on library import, but only when they are used for the first time, which improves the library's import time.

src/xulbux/code.py

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@ def add_indent(code: str, indent: int) -> str:
1818
--------------------------------------------------------------------------
1919
- `code` -⠀the code to indent
2020
- `indent` -⠀the amount of spaces to add at the beginning of each line"""
21-
if not isinstance(code, str):
22-
raise TypeError(f"The 'code' parameter must be a string, got {type(code)}")
23-
if not isinstance(indent, int):
24-
raise TypeError(f"The 'indent' parameter must be an integer, got {type(indent)}")
25-
elif indent < 0:
21+
if indent < 0:
2622
raise ValueError(f"The 'indent' parameter must be non-negative, got {indent!r}")
2723

2824
return "\n".join(" " * indent + line for line in code.splitlines())
@@ -32,9 +28,6 @@ def get_tab_spaces(code: str) -> int:
3228
"""Will try to get the amount of spaces used for indentation.\n
3329
----------------------------------------------------------------
3430
- `code` -⠀the code to analyze"""
35-
if not isinstance(code, str):
36-
raise TypeError(f"The 'code' parameter must be a string, got {type(code)}")
37-
3831
indents = [len(line) - len(line.lstrip()) for line in String.get_lines(code, remove_empty_lines=True)]
3932
return min(non_zero_indents) if (non_zero_indents := [i for i in indents if i > 0]) else 0
4033

@@ -45,14 +38,8 @@ def change_tab_size(code: str, new_tab_size: int, remove_empty_lines: bool = Fal
4538
- `code` -⠀the code to modify the tab size of
4639
- `new_tab_size` -⠀the new amount of spaces per tab
4740
- `remove_empty_lines` -⠀is true, empty lines will be removed in the process"""
48-
if not isinstance(code, str):
49-
raise TypeError(f"The 'code' parameter must be a string, got {type(code)}")
50-
if not isinstance(new_tab_size, int):
51-
raise TypeError(f"The 'new_tab_size' parameter must be an integer, got {type(new_tab_size)}")
52-
elif new_tab_size < 0:
41+
if new_tab_size < 0:
5342
raise ValueError(f"The 'new_tab_size' parameter must be non-negative, got {new_tab_size!r}")
54-
if not isinstance(remove_empty_lines, bool):
55-
raise TypeError(f"The 'remove_empty_lines' parameter must be a boolean, got {type(remove_empty_lines)}")
5643

5744
code_lines = String.get_lines(code, remove_empty_lines=remove_empty_lines)
5845

@@ -73,9 +60,6 @@ def get_func_calls(code: str) -> list:
7360
"""Will try to get all function calls and return them as a list.\n
7461
-------------------------------------------------------------------
7562
- `code` -⠀the code to analyze"""
76-
if not isinstance(code, str):
77-
raise TypeError(f"The 'code' parameter must be a string, got {type(code)}")
78-
7963
nested_func_calls = []
8064

8165
for _, func_attrs in (funcs := _rx.findall(r"(?i)" + Regex.func_call(), code)):
@@ -90,12 +74,8 @@ def is_js(code: str, funcs: set[str] = {"__", "$t", "$lang"}) -> bool:
9074
-------------------------------------------------------------
9175
- `code` -⠀the code to analyze
9276
- `funcs` -⠀a list of custom function names to check for"""
93-
if not isinstance(code, str):
94-
raise TypeError(f"The 'code' parameter must be a string, got {type(code)}")
95-
elif len(code.strip()) < 3:
77+
if len(code.strip()) < 3:
9678
return False
97-
if not isinstance(funcs, set):
98-
raise TypeError(f"The 'funcs' parameter must be a set, got {type(funcs)}")
9979

10080
for func in funcs:
10181
if _rx.match(r"^[\s\n]*" + _rx.escape(func) + r"\([^\)]*\)[\s\n]*$", code):
@@ -125,22 +105,22 @@ def is_js(code: str, funcs: set[str] = {"__", "$t", "$lang"}) -> bool:
125105

126106
js_score = 0
127107
funcs_pattern = r"(" + "|".join(_rx.escape(f) for f in funcs) + r")" + Regex.brackets("()")
128-
js_indicators = [(r"\b(var|let|const)\s+[\w_$]+", 2), # JS variable declarations
129-
(r"\$[\w_$]+\s*=", 2), # jQuery-style variables
130-
(r"\$[\w_$]+\s*\(", 2), # jQuery function calls
131-
(r"\bfunction\s*[\w_$]*\s*\(", 2), # Function declarations
132-
(r"[\w_$]+\s*=\s*function\s*\(", 2), # Function assignments
133-
(r"\b[\w_$]+\s*=>\s*[\{\(]", 2), # Arrow functions
134-
(r"\(function\s*\(\)\s*\{", 2), # IIFE pattern
135-
(funcs_pattern, 2), # Custom predefined functions
136-
(r"\b(true|false|null|undefined)\b", 1), # JS literals
137-
(r"===|!==|\+\+|--|\|\||&&", 1.5), # JS-specific operators
138-
(r"\bnew\s+[\w_$]+\s*\(", 1.5), # Object instantiation with new
139-
(r"\b(document|window|console|Math|Array|Object|String|Number)\.", 2), # JS objects
140-
(r"\basync\s+function|\bawait\b", 2), # Async/await
141-
(r"\b(if|for|while|switch)\s*\([^)]*\)\s*\{", 1), # Control structures with braces
142-
(r"\btry\s*\{[^}]*\}\s*catch\s*\(", 1.5), # Try-catch
143-
(r";[\s\n]*$", 0.5), # Semicolon line endings
108+
js_indicators = [(r"\b(var|let|const)\s+[\w_$]+", 2), # JS VARIABLE DECLARATIONS
109+
(r"\$[\w_$]+\s*=", 2), # jQuery-STYLE VARIABLES
110+
(r"\$[\w_$]+\s*\(", 2), # jQuery FUNCTION CALLS
111+
(r"\bfunction\s*[\w_$]*\s*\(", 2), # FUNCTION DECLARATIONS
112+
(r"[\w_$]+\s*=\s*function\s*\(", 2), # FUNCTION ASSIGNMENTS
113+
(r"\b[\w_$]+\s*=>\s*[\{\(]", 2), # ARROW FUNCTIONS
114+
(r"\(function\s*\(\)\s*\{", 2), # IIFE PATTERN
115+
(funcs_pattern, 2), # CUSTOM PREDEFINED FUNCTIONS
116+
(r"\b(true|false|null|undefined)\b", 1), # JS LITERALS
117+
(r"===|!==|\+\+|--|\|\||&&", 1.5), # JS-SPECIFIC OPERATORS
118+
(r"\bnew\s+[\w_$]+\s*\(", 1.5), # OBJECT INSTANTIATION WITH NEW
119+
(r"\b(document|window|console|Math|Array|Object|String|Number)\.", 2), # JS OBJECTS
120+
(r"\basync\s+function|\bawait\b", 2), # ASYNC/AWAIT
121+
(r"\b(if|for|while|switch)\s*\([^)]*\)\s*\{", 1), # CONTROL STRUCTURES WITH BRACES
122+
(r"\btry\s*\{[^}]*\}\s*catch\s*\(", 1.5), # TRY-CATCH
123+
(r";[\s\n]*$", 0.5), # SEMICOLON LINE ENDINGS
144124
]
145125

146126
line_endings = [line.strip() for line in code.splitlines() if line.strip()]

src/xulbux/color.py

Lines changed: 17 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,12 @@ def __init__(self, r: int, g: int, b: int, a: Optional[float] = None, _validate:
5555
self.r, self.g, self.b, self.a = r, g, b, a
5656
return
5757

58-
if any(isinstance(x, rgba) for x in (r, g, b)):
59-
raise ValueError("Color is already an rgba() color object.")
60-
if not all(isinstance(x, int) and (0 <= x <= 255) for x in (r, g, b)):
58+
if not all((0 <= x <= 255) for x in (r, g, b)):
6159
raise ValueError(
6260
f"The 'r', 'g' and 'b' parameters must be integers in range [0, 255] inclusive, got {r=} {g=} {b=}"
6361
)
64-
if a is not None:
65-
if not isinstance(a, float):
66-
raise TypeError(f"The 'a' parameter must be a float, got {type(a)}")
67-
elif not (0.0 <= a <= 1.0):
68-
raise ValueError(f"The 'a' parameter must be in range [0.0, 1.0] inclusive, got {a!r}")
62+
if a is not None and not (0.0 <= a <= 1.0):
63+
raise ValueError(f"The 'a' parameter must be in range [0.0, 1.0] inclusive, got {a!r}")
6964

7065
self.r, self.g, self.b = r, g, b
7166
self.a = None if a is None else (1.0 if a > 1.0 else float(a))
@@ -317,17 +312,12 @@ def __init__(self, h: int, s: int, l: int, a: Optional[float] = None, _validate:
317312
self.h, self.s, self.l, self.a = h, s, l, a
318313
return
319314

320-
if any(isinstance(x, hsla) for x in (h, s, l)):
321-
raise ValueError("Color is already a hsla() color object.")
322-
if not (isinstance(h, int) and (0 <= h <= 360)):
323-
raise ValueError(f"The 'h' parameter must be an integer in range [0, 360] inclusive, got {h!r}")
324-
if not all(isinstance(x, int) and (0 <= x <= 100) for x in (s, l)):
325-
raise ValueError(f"The 's' and 'l' parameters must be integers in range [0, 100] inclusive, got {s=} {l=}")
326-
if a is not None:
327-
if not isinstance(a, float):
328-
raise TypeError(f"The 'a' parameter must be a float, got {type(a)}")
329-
elif not (0.0 <= a <= 1.0):
330-
raise ValueError(f"The 'a' parameter must be in range [0.0, 1.0] inclusive, got {a!r}")
315+
if not (0 <= h <= 360):
316+
raise ValueError(f"The 'h' parameter must be in range [0, 360] inclusive, got {h!r}")
317+
if not all((0 <= x <= 100) for x in (s, l)):
318+
raise ValueError(f"The 's' and 'l' parameters must be in range [0, 100] inclusive, got {s=} {l=}")
319+
if a is not None and not (0.0 <= a <= 1.0):
320+
raise ValueError(f"The 'a' parameter must be in range [0.0, 1.0] inclusive, got {a!r}")
331321

332322
self.h, self.s, self.l = h, s, l
333323
self.a = None if a is None else (1.0 if a > 1.0 else float(a))
@@ -860,9 +850,6 @@ def is_valid_hsla(color: AnyHsla, allow_alpha: bool = True) -> bool:
860850
-----------------------------------------------------------------
861851
- `color` -⠀the color to check (can be in any supported format)
862852
- `allow_alpha` -⠀whether to allow alpha channel in the color"""
863-
if not isinstance(allow_alpha, bool):
864-
raise TypeError(f"The 'new_tab_size' parameter must be an boolean, got {type(allow_alpha)}")
865-
866853
try:
867854
if isinstance(color, hsla):
868855
return True
@@ -907,11 +894,6 @@ def is_valid_hexa(
907894
- `color` -⠀the color to check (can be in any supported format)
908895
- `allow_alpha` -⠀whether to allow alpha channel in the color
909896
- `get_prefix` -⠀if true, the prefix used in the color (if any) is returned along with validity"""
910-
if not isinstance(allow_alpha, bool):
911-
raise TypeError(f"The 'new_tab_size' parameter must be an boolean, got {type(allow_alpha)}")
912-
if not isinstance(get_prefix, bool):
913-
raise TypeError(f"The 'get_prefix' parameter must be an boolean, got {type(get_prefix)}")
914-
915897
try:
916898
if isinstance(color, hexa):
917899
return (True, "#") if get_prefix else True
@@ -938,9 +920,6 @@ def is_valid(color: AnyRgba | AnyHsla | AnyHexa, allow_alpha: bool = True) -> bo
938920
-------------------------------------------------------------------
939921
- `color` -⠀the color to check (can be in any supported format)
940922
- `allow_alpha` -⠀whether to allow alpha channel in the color"""
941-
if not isinstance(allow_alpha, bool):
942-
raise TypeError(f"The 'new_tab_size' parameter must be an boolean, got {type(allow_alpha)}")
943-
944923
return bool(
945924
Color.is_valid_rgba(color, allow_alpha) \
946925
or Color.is_valid_hsla(color, allow_alpha) \
@@ -1022,11 +1001,6 @@ def str_to_rgba(string: str, only_first: bool = False) -> Optional[rgba | list[r
10221001
---------------------------------------------------------------------------------------------------------------
10231002
- `string` -⠀the string to search for RGBA colors
10241003
- `only_first` -⠀if true, only the first found color will be returned, otherwise a list of all found colors"""
1025-
if not isinstance(string, str):
1026-
raise TypeError(f"The 'string' parameter must be a string, got {type(string)}")
1027-
if not isinstance(only_first, bool):
1028-
raise TypeError(f"The 'only_first' parameter must be an boolean, got {type(only_first)}")
1029-
10301004
if only_first:
10311005
if not (match := _re.search(Regex.rgba_str(allow_alpha=True), string)):
10321006
return None
@@ -1071,12 +1045,10 @@ def rgba_to_hex_int(
10711045
This could affect the color a little bit, but will make sure, that it won't be interpreted
10721046
as a completely different color, when initializing it as a `hexa()` color or changing it
10731047
back to RGBA using `Color.hex_int_to_rgba()`."""
1074-
if not all(isinstance(c, int) and 0 <= c <= 255 for c in (r, g, b)):
1048+
if not all((0 <= c <= 255) for c in (r, g, b)):
10751049
raise ValueError(f"The 'r', 'g' and 'b' parameters must be integers in [0, 255], got {r=} {g=} {b=}")
1076-
if a is not None and not (isinstance(a, float) and 0 <= a <= 1):
1050+
if a is not None and not (0.0 <= a <= 1.0):
10771051
raise ValueError(f"The 'a' parameter must be a float in [0.0, 1.0] or None, got {a!r}")
1078-
if not isinstance(preserve_original, bool):
1079-
raise TypeError(f"The 'preserve_original' parameter must be an boolean, got {type(preserve_original)}")
10801052

10811053
r = max(0, min(255, int(r)))
10821054
g = max(0, min(255, int(g)))
@@ -1104,11 +1076,7 @@ def hex_int_to_rgba(hex_int: int, preserve_original: bool = False) -> rgba:
11041076
If the red channel is `1` after conversion, it will be set to `0`, because when converting
11051077
from RGBA to a HEX integer, the first `0` will be set to `1` to preserve leading zeros.
11061078
This is the correction, so the color doesn't even look slightly different."""
1107-
if not isinstance(hex_int, int):
1108-
raise TypeError(f"The 'hex_int' parameter must be an integer, got {type(hex_int)}")
1109-
if not isinstance(preserve_original, bool):
1110-
raise TypeError(f"The 'preserve_original' parameter must be an boolean, got {type(preserve_original)}")
1111-
elif not 0 <= hex_int <= 0xFFFFFFFF:
1079+
if not (0 <= hex_int <= 0xFFFFFFFF):
11121080
raise ValueError(f"Expected HEX integer in range [0x000000, 0xFFFFFFFF] inclusive, got 0x{hex_int:X}")
11131081

11141082
if len(hex_str := f"{hex_int:X}") <= 6:
@@ -1154,12 +1122,10 @@ def luminance(
11541122
* `"wcag3"` Draft WCAG 3.0 standard with improved coefficients
11551123
* `"simple"` Simple arithmetic mean (less accurate)
11561124
* `"bt601"` ITU-R BT.601 standard (older TV standard)"""
1157-
if not all(isinstance(c, int) and 0 <= c <= 255 for c in (r, g, b)):
1125+
if not all(0 <= c <= 255 for c in (r, g, b)):
11581126
raise ValueError(f"The 'r', 'g' and 'b' parameters must be integers in [0, 255], got {r=} {g=} {b=}")
11591127
if output_type not in {int, float, None}:
11601128
raise TypeError(f"The 'output_type' parameter must be either 'int', 'float' or 'None', got {output_type!r}")
1161-
if method not in {"wcag2", "wcag3", "simple", "bt601"}:
1162-
raise ValueError(f"The 'method' parameter must be one of 'wcag2', 'wcag3', 'simple' or 'bt601', got {method!r}")
11631129

11641130
_r, _g, _b = r / 255.0, g / 255.0, b / 255.0
11651131

@@ -1190,9 +1156,7 @@ def _linearize_srgb(c: float) -> float:
11901156
"""Helper method to linearize sRGB component following the WCAG standard.\n
11911157
----------------------------------------------------------------------------
11921158
- `c` -⠀the sRGB component value in range [0.0, 1.0] inclusive"""
1193-
if not isinstance(c, float):
1194-
raise TypeError(f"The 'c' parameter must be a float, got {type(c)}")
1195-
elif not 0.0 <= c <= 1.0:
1159+
if not (0.0 <= c <= 1.0):
11961160
raise ValueError(f"The 'c' parameter must be in range [0.0, 1.0] inclusive, got {c!r}")
11971161

11981162
if c <= 0.03928:
@@ -1207,9 +1171,6 @@ def text_color_for_on_bg(text_bg_color: Rgba | Hexa) -> rgba | hexa | int:
12071171
- `text_bg_color` -⠀the background color (can be in RGBA or HEXA format)"""
12081172
was_hexa, was_int = Color.is_valid_hexa(text_bg_color), isinstance(text_bg_color, int)
12091173

1210-
if not (Color.is_valid_rgba(text_bg_color) or was_hexa):
1211-
raise ValueError(f"The 'text_bg_color' parameter must be a valid RGBA or HEXA color, got {text_bg_color!r}")
1212-
12131174
text_bg_color = Color.to_rgba(text_bg_color)
12141175
brightness = 0.2126 * text_bg_color[0] + 0.7152 * text_bg_color[1] + 0.0722 * text_bg_color[2]
12151176

@@ -1230,16 +1191,13 @@ def adjust_lightness(color: Rgba | Hexa, lightness_change: float) -> rgba | hexa
12301191
in range `-1.0` (darken by 100%) and `1.0` (lighten by 100%)"""
12311192
was_hexa = Color.is_valid_hexa(color)
12321193

1233-
if not (Color.is_valid_rgba(color) or was_hexa):
1234-
raise ValueError(f"The 'color' parameter must be a valid RGBA or HEXA color, got {color!r}")
1235-
if not isinstance(lightness_change, float):
1236-
raise TypeError(f"The 'lightness_change' parameter must be a float, got {type(lightness_change)}")
1237-
elif not -1.0 <= lightness_change <= 1.0:
1194+
if not (-1.0 <= lightness_change <= 1.0):
12381195
raise ValueError(
12391196
f"The 'lightness_change' parameter must be in range [-1.0, 1.0] inclusive, got {lightness_change!r}"
12401197
)
12411198

12421199
hsla_color: hsla = Color.to_hsla(color)
1200+
12431201
h, s, l, a = (
12441202
int(hsla_color[0]), int(hsla_color[1]), int(hsla_color[2]), \
12451203
hsla_color[3] if Color.has_alpha(hsla_color) else None
@@ -1260,11 +1218,7 @@ def adjust_saturation(color: Rgba | Hexa, saturation_change: float) -> rgba | he
12601218
in range `-1.0` (saturate by 100%) and `1.0` (desaturate by 100%)"""
12611219
was_hexa = Color.is_valid_hexa(color)
12621220

1263-
if not (Color.is_valid_rgba(color) or was_hexa):
1264-
raise ValueError(f"The 'color' parameter must be a valid RGBA or HEXA color, got {color!r}")
1265-
if not isinstance(saturation_change, float):
1266-
raise TypeError(f"The 'saturation_change' parameter must be a float, got {type(saturation_change)}")
1267-
elif not -1.0 <= saturation_change <= 1.0:
1221+
if not (-1.0 <= saturation_change <= 1.0):
12681222
raise ValueError(
12691223
f"The 'saturation_change' parameter must be in range [-1.0, 1.0] inclusive, got {saturation_change!r}"
12701224
)

0 commit comments

Comments
 (0)