Skip to content

Commit c125d14

Browse files
committed
update 1.8.3
1 parent 27a3a85 commit c125d14

8 files changed

Lines changed: 42 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
# <br><b>Changelog</b><br>
1616

17+
## ... `v1.8.3`
18+
* adjusted the look of the prompts and inputs of the `System.check_libs()` method
19+
* adjusted error messages throughout the whole library to all be structured about the same
20+
1721
## 11.09.2025 `v1.8.2`
1822
* the client command `xulbux-help` now tells you that there's a newer version of the library available, if you're not using the latest version
1923
* added two new params to `Console.input()`:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "xulbux"
7-
version = "1.8.2"
7+
version = "1.8.3"
88
authors = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
99
maintainers = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
1010
description = "A Python library which includes lots of helpful classes, types, and functions aiming to make common programming tasks simpler."

src/xulbux/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.8.2"
1+
__version__ = "1.8.3"
22

33
__author__ = "XulbuX"
44
__email__ = "xulbux.real@gmail.com"

src/xulbux/data.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,24 @@ def serialize_bytes(data: bytes | bytearray) -> dict[str, str]:
2424
except UnicodeDecodeError:
2525
pass
2626
return {key: _base64.b64encode(data).decode("utf-8"), "encoding": "base64"}
27-
raise TypeError("Unsupported data type")
27+
raise TypeError(f"Unsupported data type '{type(data)}'")
2828

2929
@staticmethod
3030
def deserialize_bytes(obj: dict[str, str]) -> bytes | bytearray:
31-
"""Converts a JSON-compatible bytes/bytearray format (dictionary) back to its original type."""
31+
"""Tries to converts a JSON-compatible bytes/bytearray format (dictionary) back to its original type.\n
32+
--------------------------------------------------------------------------------------------------------
33+
If the serialized object was created with `Data.serialize_bytes()`, it will work.
34+
If it fails to decode the data, it will raise a `ValueError`."""
3235
for key in ("bytes", "bytearray"):
3336
if key in obj and "encoding" in obj:
3437
if obj["encoding"] == "utf-8":
3538
data = obj[key].encode("utf-8")
3639
elif obj["encoding"] == "base64":
3740
data = _base64.b64decode(obj[key].encode("utf-8"))
3841
else:
39-
raise ValueError("Unknown encoding method")
42+
raise ValueError(f"Unknown encoding method '{obj['encoding']}'")
4043
return bytearray(data) if key == "bytearray" else data
41-
raise ValueError("Invalid serialized data")
44+
raise ValueError(f"Invalid serialized data: {obj}")
4245

4346
@staticmethod
4447
def chars_count(data: DataStructure) -> int:
@@ -387,7 +390,7 @@ def update_nested(data: DataStructure, path: list[int], value: Any) -> DataStruc
387390

388391
valid_entries = [(path_id, new_val) for path_id, new_val in update_values.items()]
389392
if not valid_entries:
390-
raise ValueError(f"No valid update_values found in dictionary: {update_values}")
393+
raise ValueError(f"No valid 'update_values' found in dictionary: {update_values}")
391394
for path_id, new_val in valid_entries:
392395
path = Data.__sep_path_id(path_id)
393396
data = update_nested(data, path, new_val)
@@ -581,7 +584,7 @@ def print(
581584
@staticmethod
582585
def __sep_path_id(path_id: str) -> list[int]:
583586
if path_id.count(">") != 1:
584-
raise ValueError(f"Invalid path ID: {path_id}")
587+
raise ValueError(f"Invalid path ID '{path_id}'")
585588
id_part_len = int(path_id.split(">")[0])
586589
path_ids_str = path_id.split(">")[1]
587590
return [int(path_ids_str[i:i + id_part_len]) for i in range(0, len(path_ids_str), id_part_len)]

src/xulbux/json.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def create_nested_path(data_obj: dict, path_keys: list[str], value: Any) -> dict
134134
current[idx] = [] if next_key.isdigit() else {}
135135
current = current[idx]
136136
else:
137-
raise TypeError(f"Cannot navigate through {type(current).__name__}")
137+
raise TypeError(f"Cannot navigate through '{type(current).__name__}'")
138138
return data_obj
139139

140140
update = {}

src/xulbux/path.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def extend(
4646
raise_error: bool = False,
4747
use_closest_match: bool = False,
4848
) -> Optional[str]:
49-
"""Tries to locate and extend a relative path to an absolute path.\n
49+
"""Tries to resolve and extend a relative path to an absolute path.\n
5050
--------------------------------------------------------------------------------
5151
If the `rel_path` couldn't be located in predefined directories, it will be
5252
searched in the `search_in` directory/s. If the `rel_path` is still not found,

src/xulbux/system.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from .format_codes import FormatCodes
2+
from .console import Console
3+
14
from typing import Optional
25
import subprocess as _subprocess
36
import platform as _platform
@@ -68,10 +71,14 @@ def restart(prompt: object = "", wait: int = 0, continue_program: bool = False,
6871

6972
@staticmethod
7073
def check_libs(lib_names: list[str], install_missing: bool = False, confirm_install: bool = True) -> Optional[list[str]]:
71-
"""Checks if the given list of libraries are installed. If not:
72-
- If `install_missing` is false, the missing libraries will be returned as a list.
73-
- If `install_missing` is true, the missing libraries will be installed.
74-
- If `confirm_install` is true, the user will first be asked if they want to install the missing libraries."""
74+
"""Checks if the given list of libraries are installed and optionally installs missing libraries.\n
75+
------------------------------------------------------------------------------------------------------------
76+
- `lib_names` -⠀a list of library names to check
77+
- `install_missing` -⠀whether to directly missing libraries will be installed automatically using pip
78+
- `confirm_install` -⠀whether the user will be asked for confirmation before installing missing libraries\n
79+
------------------------------------------------------------------------------------------------------------
80+
If some libraries are missing or they could not be installed, their names will be returned as a list.
81+
If all libraries are installed (or were installed successfully), `None` will be returned."""
7582
missing = []
7683
for lib in lib_names:
7784
try:
@@ -83,14 +90,18 @@ def check_libs(lib_names: list[str], install_missing: bool = False, confirm_inst
8390
elif not install_missing:
8491
return missing
8592
if confirm_install:
86-
print("The following required libraries are missing:")
93+
FormatCodes.print("[b](The following required libraries are missing:)")
8794
for lib in missing:
88-
print(f"- {lib}")
89-
if input("Do you want to install them now (Y/n): ").strip().lower() not in ("", "y", "yes"):
90-
raise ImportError("Missing required libraries.")
95+
FormatCodes.print(f" [dim](•) [i]{lib}[_i]", end="\n\n")
96+
if not Console.confirm("Do you want to install them now?"):
97+
return missing
9198
try:
92-
_subprocess.check_call([_sys.executable, "-m", "pip", "install"] + missing)
93-
return None
99+
for lib in missing:
100+
_subprocess.check_call([_sys.executable, "-m", "pip", "install", lib])
101+
missing.remove(lib)
102+
if len(missing) == 0:
103+
return None
104+
return missing
94105
except _subprocess.CalledProcessError:
95106
return missing
96107

tests/test_system.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ def test_check_libs_nonexistent_module():
3636
@patch('builtins.input', return_value='n') # Decline installation
3737
def test_check_libs_decline_install(mock_input, mock_subprocess):
3838
"""Test check_libs when user declines installation"""
39-
with pytest.raises(ImportError, match="Missing required libraries"):
40-
System.check_libs(["nonexistent_module_12345"], install_missing=True, confirm_install=True)
39+
result = System.check_libs(["nonexistent_module_12345"], install_missing=True)
40+
assert isinstance(result, list)
41+
assert "nonexistent_module_12345" in result
42+
mock_subprocess.assert_not_called()
4143

4244

4345
@pytest.mark.skipif(os.name != 'nt', reason="Windows-specific test")

0 commit comments

Comments
 (0)