alptools 2.6.1__tar.gz

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 (41) hide show
  1. alptools-2.6.1/.gitignore +8 -0
  2. alptools-2.6.1/LICENSE.txt +21 -0
  3. alptools-2.6.1/PKG-INFO +25 -0
  4. alptools-2.6.1/README.md +2 -0
  5. alptools-2.6.1/pyproject.toml +36 -0
  6. alptools-2.6.1/src/atools/__about__.py +1 -0
  7. alptools-2.6.1/src/atools/__init__.py +20 -0
  8. alptools-2.6.1/src/atools/base_tools/__init__.py +17 -0
  9. alptools-2.6.1/src/atools/base_tools/__logger.py +24 -0
  10. alptools-2.6.1/src/atools/base_tools/file/copy_file.py +43 -0
  11. alptools-2.6.1/src/atools/base_tools/file/delete_file.py +39 -0
  12. alptools-2.6.1/src/atools/base_tools/file/move_file.py +43 -0
  13. alptools-2.6.1/src/atools/base_tools/file/read_file.py +37 -0
  14. alptools-2.6.1/src/atools/base_tools/file/write_file.py +45 -0
  15. alptools-2.6.1/src/atools/base_tools/object/append_unique.py +25 -0
  16. alptools-2.6.1/src/atools/base_tools/object/object_get.py +24 -0
  17. alptools-2.6.1/src/atools/base_tools/object/object_set.py +70 -0
  18. alptools-2.6.1/src/atools/base_tools/object/object_split.py +21 -0
  19. alptools-2.6.1/src/atools/base_tools/path/check_dir.py +35 -0
  20. alptools-2.6.1/src/atools/base_tools/path/path_join.py +33 -0
  21. alptools-2.6.1/src/atools/base_tools/string/connect_str.py +35 -0
  22. alptools-2.6.1/src/atools/base_tools/string/is_number.py +18 -0
  23. alptools-2.6.1/src/atools/base_tools/string/str_add_num.py +22 -0
  24. alptools-2.6.1/src/atools/io_tools/__init__.py +7 -0
  25. alptools-2.6.1/src/atools/io_tools/clear_empty_dir.py +17 -0
  26. alptools-2.6.1/src/atools/io_tools/get_file_hash.py +9 -0
  27. alptools-2.6.1/src/atools/io_tools/reader/__debug_detector.py +32 -0
  28. alptools-2.6.1/src/atools/io_tools/reader/io_reader.py +179 -0
  29. alptools-2.6.1/src/atools/io_tools/reader/memory_view_reader.py +95 -0
  30. alptools-2.6.1/src/atools/io_tools/reader/mmap_reader.py +98 -0
  31. alptools-2.6.1/src/atools/io_tools/xor_file.py +5 -0
  32. alptools-2.6.1/src/atools/misc_tools/ayaml.py +35 -0
  33. alptools-2.6.1/src/atools/misc_tools/setup_module_path.py +12 -0
  34. alptools-2.6.1/src/atools/misc_tools/time_tick.py +13 -0
  35. alptools-2.6.1/src/atools/object_tools/__init__.py +4 -0
  36. alptools-2.6.1/src/atools/object_tools/classes/class_dict.py +62 -0
  37. alptools-2.6.1/src/atools/object_tools/classes/json_dict.py +153 -0
  38. alptools-2.6.1/src/atools/object_tools/dict_diff.py +58 -0
  39. alptools-2.6.1/src/atools/object_tools/json_dump.py +24 -0
  40. alptools-2.6.1/src/atools/object_tools/object_filter.py +12 -0
  41. alptools-2.6.1/src/atools/object_tools/object_type.py +63 -0
@@ -0,0 +1,8 @@
1
+ tests/
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[codz]
6
+ *$py.class
7
+
8
+ dist/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AhAlpha
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: alptools
3
+ Version: 2.6.1
4
+ Summary: simple tools for alpha projects
5
+ Author: AhAlpha
6
+ License-Expression: MIT
7
+ License-File: LICENSE.txt
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Requires-Python: >=3.8
20
+ Provides-Extra: full
21
+ Requires-Dist: pyyaml>=6; extra == 'full'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # alpha_tools
25
+ simple tools for alpha projects
@@ -0,0 +1,2 @@
1
+ # alpha_tools
2
+ simple tools for alpha projects
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "alptools"
7
+ dynamic = ["version"]
8
+ description = 'simple tools for alpha projects'
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = "MIT"
12
+ keywords = []
13
+ authors = [{ name = "AhAlpha" }]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Programming Language :: Python :: Implementation :: CPython",
25
+ "Programming Language :: Python :: Implementation :: PyPy",
26
+ ]
27
+ dependencies = []
28
+
29
+ [project.optional-dependencies]
30
+ full = ["PyYAML>=6"]
31
+
32
+ [tool.hatch.version]
33
+ path = "src/atools/__about__.py"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["src/atools"]
@@ -0,0 +1 @@
1
+ __version__ = "2.6.1"
@@ -0,0 +1,20 @@
1
+ from .__about__ import __version__
2
+
3
+ from .base_tools import path_join as APATH
4
+ from .base_tools import check_dir as ADIR
5
+
6
+ from .base_tools import read_file as ARF
7
+ from .base_tools import write_file as AWF
8
+ from .base_tools import move_file as AMF
9
+ from .base_tools import copy_file as ACF
10
+ from .base_tools import delete_file as ADF
11
+
12
+ from .base_tools import object_get as AGET
13
+ from .base_tools import object_set as ASET
14
+ from .base_tools import object_split as ASPLIT
15
+
16
+ from .base_tools import append_unique as ADUQ
17
+
18
+ # from .base_tools import connect_str_main as AJOIN
19
+ from .base_tools import str_add_num as ASAN
20
+ from .base_tools import is_number as ASNUM
@@ -0,0 +1,17 @@
1
+ from .object.append_unique import append_unique
2
+ from .object.object_get import object_get
3
+ from .object.object_set import object_set
4
+ from .object.object_split import object_split
5
+
6
+ from .file.copy_file import copy_file
7
+ from .file.delete_file import delete_file
8
+ from .file.move_file import move_file
9
+ from .file.read_file import read_file
10
+ from .file.write_file import write_file
11
+
12
+ from .path.check_dir import check_dir
13
+ from .path.path_join import path_join
14
+
15
+ # from .string.connect_str import connect_str
16
+ from .string.is_number import is_number
17
+ from .string.str_add_num import str_add_num
@@ -0,0 +1,24 @@
1
+ import inspect
2
+
3
+
4
+ def info(*message, level=2) -> None:
5
+ """
6
+ Print the current stack trace as a formatted string.
7
+
8
+ Returns:
9
+ - None
10
+ """
11
+ frame = inspect.stack()[level]
12
+ print(f" \033[32m[INF]\033[0m", f"\033[35m{frame.code_context[0].split("(")[0].strip()}()\033[0m:", *message)
13
+
14
+
15
+
16
+ def warn(*message, level=2) -> None:
17
+ """
18
+ Print the current stack trace as a formatted string.
19
+
20
+ Returns:
21
+ - None
22
+ """
23
+ frame = inspect.stack()[level]
24
+ print(f" \033[33m[WRN]\033[0m", f"\033[35m{frame.code_context[0].split("(")[0].strip()}()\033[0m:", *message, f'File \033[35m"{frame.filename}"\033[0m, line \033[35m{frame.lineno}\033[0m')
@@ -0,0 +1,43 @@
1
+ import os
2
+ import shutil
3
+
4
+ from ..__logger import warn
5
+ from ..path.check_dir import check_dir
6
+
7
+
8
+ def copy_file(bfile: str, afile: str, show_warn: bool = True) -> int:
9
+ """
10
+ Copies a file or directory from one location to another.
11
+
12
+ Parameters:
13
+ - bfile (str): The source file or directory path to copy.
14
+ - afile (str): The destination file or directory path.
15
+ - show_warn (bool): If True, prints warnings when the operation fails. Defaults to True.
16
+
17
+ Returns:
18
+ - int: Status code indicating the result of the operation.
19
+ 0: Success.
20
+ 1: Source file or directory not found.
21
+ 2: Permission denied.
22
+ 3: Other errors occurred.
23
+ """
24
+ try:
25
+ if os.path.isfile(bfile):
26
+ check_dir(afile, is_filename=True, logger_level=3)
27
+ shutil.copy(bfile, afile)
28
+ elif os.path.isdir(bfile):
29
+ check_dir(afile, logger_level=3)
30
+ shutil.copytree(bfile, afile, dirs_exist_ok=True)
31
+ else:
32
+ if show_warn:
33
+ warn(f'FileNotFoundError: Item "{bfile}" not found.')
34
+ return -1
35
+ return 0
36
+ except PermissionError:
37
+ if show_warn:
38
+ warn(f'PermissionError: Permission denied to copy "{bfile}".')
39
+ return -2
40
+ except Exception as e:
41
+ if show_warn:
42
+ warn(f'{e} "{bfile}".')
43
+ return -3
@@ -0,0 +1,39 @@
1
+ import os
2
+ import shutil
3
+
4
+ from ..__logger import warn
5
+
6
+
7
+ def delete_file(file_path: str, show_warn: bool = True) -> int:
8
+ """
9
+ Deletes a file or directory.
10
+
11
+ Parameters:
12
+ - file_path (str): The path to the file or directory to delete.
13
+ - show_warn (bool): If True, prints warnings when the operation fails. Defaults to True.
14
+
15
+ Returns:
16
+ - int: Status code indicating the result of the operation.
17
+ 0: Success.
18
+ 1: File or directory not found.
19
+ 2: Permission denied.
20
+ 3: Other errors occurred.
21
+ """
22
+ try:
23
+ if os.path.isfile(file_path):
24
+ os.remove(file_path)
25
+ elif os.path.isdir(file_path):
26
+ shutil.rmtree(file_path)
27
+ else:
28
+ if show_warn:
29
+ warn(f'FileNotFoundError: Item "{file_path}" not found.')
30
+ return -1
31
+ return 0
32
+ except PermissionError:
33
+ if show_warn:
34
+ warn(f'PermissionError: Permission denied to delete "{file_path}".')
35
+ return -2
36
+ except Exception as e:
37
+ if show_warn:
38
+ warn(f'{e} File "{file_path}".')
39
+ return -3
@@ -0,0 +1,43 @@
1
+ import os
2
+ import shutil
3
+
4
+ from ..__logger import warn
5
+ from ..path.check_dir import check_dir
6
+
7
+
8
+ def move_file(bfile: str, afile: str, show_warn: bool = True):
9
+ """
10
+ Moves a file or directory from one location to another.
11
+
12
+ Parameters:
13
+ - bfile (str): The source file or directory path to move.
14
+ - afile (str): The destination file or directory path.
15
+ - show_warn (bool): If True, prints warnings when the operation fails. Defaults to True.
16
+
17
+ Returns:
18
+ - int: Status code indicating the result of the operation.
19
+ 0: Success.
20
+ 1: Source file or directory not found.
21
+ 2: Permission denied.
22
+ 3: Other errors occurred.
23
+ """
24
+ try:
25
+ if os.path.isfile(bfile):
26
+ check_dir(afile, is_filename=True, logger_level=3)
27
+ shutil.move(bfile, afile)
28
+ elif os.path.isdir(bfile):
29
+ check_dir(afile, is_filename=True, logger_level=3)
30
+ shutil.move(bfile, afile)
31
+ else:
32
+ if show_warn:
33
+ warn(f'Item "{bfile}" not found.')
34
+ return -1
35
+ return 0
36
+ except PermissionError:
37
+ if show_warn:
38
+ warn(f'PermissionError: Permission denied to move "{bfile}".')
39
+ return -2
40
+ except Exception as e:
41
+ if show_warn:
42
+ warn(f'{e} File "{bfile}".')
43
+ return -3
@@ -0,0 +1,37 @@
1
+ import os
2
+ import json
3
+ from typing import Any
4
+
5
+ from ..__logger import warn
6
+
7
+
8
+ def read_file(file_path: str, encoding: str = "utf-8", with_json: bool = False, with_bytes: bool = False, default: Any = None, show_warn: bool = True):
9
+ """
10
+ Reads the content of a file with optional JSON parsing or byte reading.
11
+
12
+ Parameters:
13
+ - file_path (str): The path to the file to be read.
14
+ - with_json (bool): If True, attempts to parse the file content as JSON.
15
+ - with_bytes (bool): If True, reads the file in binary mode.
16
+ - default (any): The default value to return if the file does not exist or an error occurs.
17
+ - show_warn (bool): If True, prints a warning message when the file is not found or cannot be read.
18
+
19
+ Returns:
20
+ - any: The content of the file, parsed JSON object, or the default `default` value.
21
+ """
22
+ if not os.path.exists(file_path):
23
+ if show_warn:
24
+ warn(f'FileNotFoundError: File "{file_path}" not found.')
25
+ return default
26
+ if with_bytes:
27
+ with open(file_path, "rb") as f:
28
+ return f.read()
29
+ with open(file_path, "r", encoding=encoding) as f:
30
+ if with_json:
31
+ try:
32
+ return json.load(f)
33
+ except Exception as e:
34
+ if show_warn:
35
+ warn(f'JSONDecodeError: File "{file_path}" is not a valid JSON file ({e}).')
36
+ return default
37
+ return f.read()
@@ -0,0 +1,45 @@
1
+ import os
2
+ import json
3
+ from typing import Any, Literal
4
+
5
+ from ..path.check_dir import check_dir
6
+
7
+
8
+ class BytesEncoder(json.JSONEncoder):
9
+ def default(self, obj):
10
+ if isinstance(obj, bytes):
11
+ return " ".join(f"{byte:02X}" for byte in obj)
12
+ return super().default(obj)
13
+
14
+
15
+ def write_file(file_path: str, content: Any = "", encoding: str = "utf-8", as_json: bool | int | Literal["no-format"] = False, as_bytes: bool = False):
16
+ """
17
+ Writes content to a file with optional JSON formatting or binary mode.
18
+
19
+ Parameters:
20
+ - file_path (str): The path to the file to be written.
21
+ - content (any): The content to write to the file. Can be a string, JSON-serializable object, or bytes.
22
+ - encoding (str): The encoding to use when writing text content. Defaults to "utf-8".
23
+ - as_json (bool): If True, serializes the content as JSON before writing.
24
+ - as_bytes (bool): If True, writes the content in binary mode.
25
+
26
+ Returns:
27
+ - None
28
+ """
29
+ if os.path.dirname(file_path) != "":
30
+ check_dir(os.path.dirname(file_path), logger_level=3)
31
+ if as_bytes or isinstance(content, bytes) or isinstance(content, bytearray):
32
+ with open(file_path, "wb") as f:
33
+ f.write(content)
34
+ return
35
+ if as_json == 2 or as_json == "no-format":
36
+ with open(file_path, "w", encoding=encoding) as f:
37
+ json.dump(content, f, ensure_ascii=False, cls=BytesEncoder)
38
+ return
39
+ if as_json or isinstance(content, dict) or isinstance(content, list):
40
+ with open(file_path, "w", encoding=encoding) as f:
41
+ json.dump(content, f, ensure_ascii=False, indent=2, separators=(",", ": "), cls=BytesEncoder)
42
+ return
43
+ with open(file_path, "w", encoding=encoding) as f:
44
+ f.write(content)
45
+ return
@@ -0,0 +1,25 @@
1
+ from typing import Any, Optional, Callable
2
+
3
+
4
+ def append_unique(target_list: list, value: Any, filter_func: Optional[Callable[[Any], bool]] = None, mapper_func: Optional[Callable[[Any], Any]] = None) -> list:
5
+ """
6
+ Append an item to a list if it is unique, with optional filtering and mapping.
7
+
8
+ Parameters:
9
+ - target_list (list): The list to which the item will be appended.
10
+ - value (Any): The item to append. Can be a single item or an iterable (list, tuple, set, or dict).
11
+ - filter_func (Callable[[Any], bool], optional): A function to filter items. Only items passing the filter will be appended.
12
+ - mapper_func (Callable[[Any], Any], optional): A function to transform the item before appending.
13
+
14
+ Returns:
15
+ - list: The updated list with the unique item(s) appended.
16
+ """
17
+ if isinstance(value, (list, tuple, set, dict)):
18
+ for item in value:
19
+ append_unique(target_list, item, filter_func, mapper_func)
20
+ else:
21
+ item = mapper_func(value) if mapper_func else value
22
+ if item not in target_list:
23
+ if not filter_func or filter_func(item):
24
+ target_list.append(item)
25
+ return target_list
@@ -0,0 +1,24 @@
1
+ from typing import Any
2
+
3
+
4
+ def object_get(base_object: dict | list, pos: str | tuple | list, default: Any = None) -> Any:
5
+ """
6
+ Retrieve a value from a nested dictionary or list using a key or sequence of keys.
7
+
8
+ Parameters:
9
+ - base_object (dict | list): The dictionary or list to retrieve the value from.
10
+ - pos (str | tuple | list): The key or sequence of keys to locate the value.
11
+ - default (any, optional): The default value to return if the key is not found. Defaults to None.
12
+
13
+ Returns:
14
+ - any: The value found at the specified position, or the default value if not found.
15
+ """
16
+ if isinstance(pos, str):
17
+ pos = (pos,)
18
+ _value = base_object
19
+ for _key in pos:
20
+ if (isinstance(_value, dict) and _key in _value) or (isinstance(_value, (list, tuple)) and _key < len(_value)):
21
+ _value = _value[_key]
22
+ else:
23
+ return default
24
+ return _value
@@ -0,0 +1,70 @@
1
+ from typing import List, Tuple, Any, Union, Literal
2
+
3
+ from ..__logger import warn
4
+
5
+
6
+ def object_set(
7
+ base_object: dict,
8
+ pos: Union[str, List[str], Tuple[str, ...]],
9
+ value: Any = None,
10
+ target_type: Literal["default", "str", "number", "list", "dict"] = "default",
11
+ unique: bool = False,
12
+ ) -> dict:
13
+ """
14
+ A function to set or update a value in a dictionary, supporting nested keys and various data types.
15
+
16
+ Parameters:
17
+ - object (dict): The dictionary to modify.
18
+ - pos (str | list | tuple): The key or nested keys where the value should be set.
19
+ - value (Any, optional): The value to set or update. Defaults to None.
20
+ - target_type (str, optional): The type of operation to perform. Options include:
21
+ - "default" or None: Directly set the value.
22
+ - "str" or "": Concatenate the value to an existing string.
23
+ - "number" or 0: Add the value to an existing number.
24
+ - "list" or []: Append the value to a list, optionally ensuring uniqueness.
25
+ - "dict" or {}: Update the value as a dictionary.
26
+ - unique (bool, optional): If True, ensures the value is unique when appending to a list. Defaults to False.
27
+
28
+ Returns:
29
+ - dict: The updated dictionary.
30
+ """
31
+ # Handle nested keys if `pos` is a tuple or list
32
+ if isinstance(pos, (list, tuple)):
33
+ if len(pos) == 0:
34
+ warn(f"Empty dict name received: {pos}.")
35
+ return base_object
36
+ elif len(pos) == 1:
37
+ pos = pos[0]
38
+ else:
39
+ curr_key, pos = pos[0], pos[1:]
40
+ if curr_key not in base_object or not isinstance(base_object[curr_key], dict):
41
+ base_object[curr_key] = {}
42
+ object_set(base_object[curr_key], pos, value, target_type, unique)
43
+ return base_object
44
+
45
+ # Create or update the value based on the `target_type` parameter
46
+ if target_type in ("default", None):
47
+ base_object[pos] = value
48
+ elif target_type in ("str", ""):
49
+ if pos not in base_object: # Init
50
+ base_object[pos] = ""
51
+ if value is not None:
52
+ base_object[pos] += value
53
+ elif target_type in ("number", 0):
54
+ if pos not in base_object: # Init
55
+ base_object[pos] = 0
56
+ if value is not None:
57
+ base_object[pos] += value
58
+ elif target_type in ("list", []):
59
+ if pos not in base_object: # Init
60
+ base_object[pos] = []
61
+ if value is not None and (not unique or value not in base_object[pos]):
62
+ base_object[pos].append(value)
63
+ elif target_type in ("dict", {}):
64
+ if pos not in base_object: # Init
65
+ base_object[pos] = {}
66
+ if value is not None:
67
+ base_object[pos].update(value)
68
+ else:
69
+ warn(f"Unknown type provided: {target_type}.")
70
+ return base_object
@@ -0,0 +1,21 @@
1
+ import math
2
+
3
+
4
+ def object_split(object: dict | list, num: int) -> list:
5
+ """
6
+ Split a dictionary or list into smaller chunks.
7
+
8
+ Parameters:
9
+ - object (dict | list): The dictionary or list to be split.
10
+ - num (int): The number of items per chunk for a dictionary, or the number of chunks for a list.
11
+
12
+ Returns:
13
+ - list: A list of smaller dictionaries or lists, depending on the input type.
14
+ """
15
+ if isinstance(object, dict):
16
+ keys = list(object.keys())
17
+ return [{k: object[k] for k in keys[i : i + num]} for i in range(0, len(keys), num)]
18
+ if isinstance(object, list):
19
+ n = math.ceil(len(object) / num)
20
+ return [object[i : i + n] for i in range(0, len(object), n)]
21
+ return []
@@ -0,0 +1,35 @@
1
+ import os
2
+
3
+ from ..__logger import info, warn
4
+
5
+
6
+ def check_dir(dir_path: str, is_filename: bool = False, show_info: bool = True, show_warn: bool = True, logger_level: int = 2) -> int:
7
+ """
8
+ Ensures the existence of a directory, creating it if necessary.
9
+
10
+ Parameters:
11
+ - dir_path (str): The directory path to check or create.
12
+ - is_filename (bool): If True, treats `dir_` as a file path and checks its parent directory.
13
+
14
+ Returns:
15
+ - int:
16
+ 0 if the directory already exists,
17
+ 1 if the directory was successfully created,
18
+ 2 if the input directory path is empty,
19
+ 3 if the directory creation failed.
20
+ """
21
+ if is_filename:
22
+ dir_path = os.path.dirname(dir_path)
23
+ if dir_path == "":
24
+ return -1
25
+ if os.path.isdir(dir_path):
26
+ return 0
27
+ os.makedirs(dir_path, exist_ok=True)
28
+ if os.path.isdir(dir_path):
29
+ if show_info:
30
+ info(f'Auto created the "{dir_path}" folder.', level=logger_level)
31
+ return 1
32
+ else:
33
+ if show_warn:
34
+ warn(f'OSError: Fail to created the "{dir_path}" folder.', level=logger_level)
35
+ return -2
@@ -0,0 +1,33 @@
1
+ import os
2
+
3
+ MAIN_PATH = os.getcwd().split("\\")
4
+
5
+
6
+
7
+ def path_join(*pos: str, auto_path: bool = True) -> str:
8
+
9
+ """
10
+ Joins multiple path components into a single path string.
11
+
12
+ Parameters:
13
+ - pos (str): One or more path components to join.
14
+ - auto_path (bool): If True, prepends the MAIN_PATH to the resulting path.
15
+
16
+ Returns:
17
+ - str: The joined path as a string.
18
+ """
19
+ return_path = []
20
+ for _i, _p in enumerate(pos):
21
+ _p = _p.replace("\\", "/").split("/")
22
+ if len(_p[0]) == 2 and _p[0][1] == ":":
23
+ return_path.clear()
24
+ elif _i == 0 and auto_path:
25
+ return_path = list(MAIN_PATH)
26
+ for _x_ in _p:
27
+ if _x_ in ".":
28
+ continue
29
+ elif _x_ == "..":
30
+ return_path.pop()
31
+ elif _x_ != "":
32
+ return_path.append(_x_)
33
+ return "/".join(return_path)
@@ -0,0 +1,35 @@
1
+ def connect_str(str_first, str_join, str_append, *str_more, _ignore=[""]):
2
+ connect_str_cache = ""
3
+ if isinstance(str_append, list):
4
+ connect_str_cache = str_first
5
+ for append_str_cache in str_append:
6
+ connect_str_cache = connect_str(connect_str_cache, str_join, append_str_cache, _ignore)
7
+ del str_append[0]
8
+ else:
9
+ if str_first not in _ignore and str_append not in _ignore:
10
+ connect_str_cache = f"{str_first}{str_join}{str_append}"
11
+ elif str_first not in _ignore:
12
+ connect_str_cache = f"{str_first}"
13
+ elif str_append not in _ignore:
14
+ connect_str_cache = f"{str_append}"
15
+ else:
16
+ connect_str_cache = f""
17
+ if str_more:
18
+ if len(str_more) >= 2:
19
+ connect_str_cache = connect_str(connect_str_cache, *str_more, _ignore=_ignore)
20
+ elif len(str_more) == 1:
21
+ print(f" [WARN] connect_str: ({connect_str_cache}, {str_more[0]}, ...) append args not Double.")
22
+ return connect_str_cache
23
+
24
+
25
+ def connect_str_main(*str_args, _ignore=[""], _igmode="None"):
26
+ if _igmode in ["None", "n"]:
27
+ return connect_str(*str_args, _ignore=_ignore)
28
+ elif _igmode in ["Head", "h"]:
29
+ return connect_str(" ", *str_args, _ignore=_ignore)[1:]
30
+ elif _igmode in ["Tail", "t"]:
31
+ return connect_str(*str_args, " ", _ignore=_ignore)[:-1]
32
+ elif _igmode in ["Head_and_Tail", "ht", "th"]:
33
+ connect_str_main_cache = connect_str_main(*str_args[:-1], _ignore=_ignore, _igmode="Head")
34
+ connect_str_main_cache = connect_str_main(connect_str_main_cache, str_args[-1], _ignore=_ignore, _igmode="Tail")
35
+ return connect_str_main_cache
@@ -0,0 +1,18 @@
1
+ import unicodedata
2
+
3
+
4
+ def is_number(str_: str) -> bool:
5
+ """
6
+ Check if a string represents a number.
7
+
8
+ Args:
9
+ - str_ (str): The input string to check.
10
+
11
+ Returns:
12
+ - bool: True if the string represents a number, False otherwise.
13
+ """
14
+ try:
15
+ float(str_) or unicodedata.numeric(str_)
16
+ return True
17
+ except (ValueError, TypeError):
18
+ return False
@@ -0,0 +1,22 @@
1
+ import re
2
+
3
+
4
+ def str_add_num(str_: str, sep_: str = "_", fill_: int = 0, start_: int = 1) -> str:
5
+ """
6
+ Increment the numeric suffix of a string, or add one if it doesn't exist.
7
+
8
+ Args:
9
+ - base_string (str): The input string to modify.
10
+ - sep_ (str): The separator between the string and the number. Default is "_".
11
+ - fill_ (int): The zero-padding width for the number. Default is 0 (no padding).
12
+ - start_ (int): The starting number to append if no numeric suffix exists. Default is 1.
13
+
14
+ Returns:
15
+ - str: The modified string with an incremented or newly added numeric suffix.
16
+ """
17
+ match = re.search(rf"{sep_}(\d+)$", str_)
18
+ if match:
19
+ str_ = re.sub(rf"{sep_}\d+$", f"{sep_}{(int(match.group(1)) + 1):0{fill_}}", str_)
20
+ else:
21
+ str_ = f"{str_}{sep_}{start_}"
22
+ return str_