py3-kit 0.0.8__py3-none-any.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.
py3_kit/__datetime.py ADDED
@@ -0,0 +1,23 @@
1
+ import arrow
2
+
3
+
4
+ def now(fmt: str = "YYYY-MM-DD HH:mm:ss") -> str:
5
+ return arrow.now().format(fmt)
6
+
7
+
8
+ def delta(start_datetime_str: str, end_datetime_str: str) -> int:
9
+ """
10
+ >>> delta("2026-01-07 08:56:48","2026-01-07 13:00:00")
11
+ 14592
12
+
13
+ Args:
14
+ start_datetime_str:
15
+ end_datetime_str:
16
+
17
+ Returns:
18
+
19
+ """
20
+ start_datetime = arrow.get(start_datetime_str).datetime
21
+ end_datetime = arrow.get(end_datetime_str).datetime
22
+ seconds = (end_datetime - start_datetime).total_seconds()
23
+ return int(seconds)
py3_kit/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ from . import __datetime as datetime
2
+ from . import __py3_execute__ as py3_execute
3
+ from . import __py3_logger__ as py3_logger
4
+ from . import __py3_web__ as py3_web
5
+ from . import __string as string
6
+ from . import _math as math
7
+ from . import _pandas as pandas
8
+ from . import _types as types
9
+ from . import assets
10
+ from . import file
11
+ from . import image
12
+ from . import list
13
+ from . import pdf
14
+ from . import project
15
+ from . import sql
16
+ from . import validators
17
+
18
+ __all__ = [
19
+ "py3_execute",
20
+ "py3_logger",
21
+ "py3_web",
22
+ "datetime",
23
+ "string",
24
+ "math",
25
+ "pandas",
26
+ "types",
27
+ "assets",
28
+ "file",
29
+ "image",
30
+ "list",
31
+ "pdf",
32
+ "project",
33
+ "sql",
34
+ "validators",
35
+ ]
@@ -0,0 +1 @@
1
+ from py3_execute import *
@@ -0,0 +1 @@
1
+ from py3_logger import *
@@ -0,0 +1 @@
1
+ from py3_web import *
py3_kit/__string.py ADDED
@@ -0,0 +1,31 @@
1
+ import re
2
+
3
+
4
+ def camel_to_snake(string: str) -> str:
5
+ """
6
+ >>> camel_to_snake("camelToSnake")
7
+ 'camel_to_snake'
8
+
9
+ Args:
10
+ string:
11
+
12
+ Returns:
13
+
14
+ """
15
+ string = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", string)
16
+ string = re.sub("([a-z0-9])([A-Z])", r"\1_\2", string).lower()
17
+ return string
18
+
19
+
20
+ def snake_to_camel(string: str) -> str:
21
+ """
22
+ >>> snake_to_camel("snake_to_camel")
23
+ 'SnakeToCamel'
24
+
25
+ Args:
26
+ string:
27
+
28
+ Returns:
29
+
30
+ """
31
+ return "".join(i.capitalize() for i in string.split("_"))
py3_kit/_math.py ADDED
@@ -0,0 +1,62 @@
1
+ import math
2
+
3
+
4
+ def int_ceil(num: float) -> int:
5
+ """
6
+ >>> int_ceil(10 / 3)
7
+ 4
8
+
9
+ Args:
10
+ num:
11
+
12
+ Returns:
13
+
14
+ """
15
+ return math.ceil(num)
16
+
17
+
18
+ def decimal_ceil(
19
+ num: float,
20
+ places: int = 2
21
+ ) -> float:
22
+ """
23
+ >>> decimal_ceil(3.14159)
24
+ 3.15
25
+
26
+ Args:
27
+ num:
28
+ places:
29
+
30
+ Returns:
31
+
32
+ """
33
+ if places >= 0:
34
+ scale = 10.0 ** places
35
+ return math.ceil(num * scale) / scale
36
+ else:
37
+ raise ValueError(
38
+ f"Invalid value for 'places': "
39
+ f"Expected `int` and >= 0, "
40
+ f"but got value: {places!r}"
41
+ )
42
+
43
+
44
+ def format_number(
45
+ number: int | float | str,
46
+ places: int = 2
47
+ ) -> str:
48
+ """
49
+ >>> format_number(3.1)
50
+ '3.10'
51
+
52
+ Args:
53
+ number:
54
+ places:
55
+
56
+ Returns:
57
+
58
+ """
59
+ integer_str, decimal_str = str(float(number)).split(".")
60
+ decimal_str = (decimal_str + "0" * (places - len(decimal_str)))[:places]
61
+ number_str = ".".join([integer_str, decimal_str])
62
+ return number_str
py3_kit/_pandas.py ADDED
@@ -0,0 +1,30 @@
1
+ import pandas as pd
2
+
3
+
4
+ def split(
5
+ df: pd.DataFrame,
6
+ num_parts: int | None = None,
7
+ part_size: int | None = None
8
+ ) -> list[pd.DataFrame]:
9
+ if df.empty:
10
+ return []
11
+
12
+ if num_parts:
13
+ avg_size = len(df) // num_parts
14
+ remainder = len(df) % num_parts
15
+
16
+ result = []
17
+ start = 0
18
+ for i in range(num_parts):
19
+ end = start + avg_size + (1 if i < remainder else 0)
20
+ result.append(df.iloc[start:end])
21
+ start = end
22
+ return result
23
+
24
+ elif part_size:
25
+ return [df.iloc[i:i + part_size] for i in range(0, len(df), part_size)]
26
+
27
+ else:
28
+ raise ValueError(
29
+ f"Either num_parts: {num_parts!r} or part_size: {part_size!r} must be provided"
30
+ )
py3_kit/_types.py ADDED
@@ -0,0 +1,50 @@
1
+ from abc import ABCMeta
2
+ from collections.abc import ItemsView, KeysView, ValuesView
3
+ from types import new_class
4
+ from typing import NamedTuple, cast
5
+
6
+ import py3_kit
7
+
8
+
9
+ class ViewClasses(NamedTuple):
10
+ ItemsView: type
11
+ KeysView: type
12
+ ValuesView: type
13
+
14
+
15
+ def create_view_classes(class_name: str) -> ViewClasses:
16
+ def _create_view_classes(base_class: type) -> type:
17
+ name = f"{class_name}{base_class.__name__}"
18
+ return new_class(
19
+ name,
20
+ (base_class,),
21
+ exec_body=lambda ns: ns.update({
22
+ "__repr__": lambda self: f"{py3_kit.string.camel_to_snake(name)}({list(self)})"
23
+ })
24
+ )
25
+
26
+ return ViewClasses(
27
+ ItemsView=_create_view_classes(ItemsView),
28
+ KeysView=_create_view_classes(KeysView),
29
+ ValuesView=_create_view_classes(ValuesView),
30
+ )
31
+
32
+
33
+ def create_subclasscheck_meta_class(
34
+ meta_class_name: str = "SubclasscheckMeta",
35
+ *,
36
+ required_all_methods: tuple[str, ...] = tuple(),
37
+ required_any_methods: tuple[str, ...] = tuple()
38
+ ) -> type[ABCMeta]:
39
+ return cast(type[ABCMeta], new_class(
40
+ meta_class_name,
41
+ (ABCMeta,),
42
+ exec_body=lambda ns: ns.update({
43
+ "__subclasscheck__": classmethod(
44
+ lambda cls, subclass:
45
+ all([callable(getattr(subclass, i, None)) for i in required_all_methods]) and
46
+ (any([callable(getattr(subclass, i, None)) for i in required_any_methods]) if required_any_methods
47
+ else True)
48
+ )
49
+ })
50
+ ))
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
py3_kit/assets.py ADDED
@@ -0,0 +1,10 @@
1
+ import inspect
2
+ import os
3
+
4
+
5
+ def get_assets_file_path(path: str) -> str:
6
+ assets_dir_path = os.path.splitext(os.path.abspath(__file__))[0]
7
+ caller_dir_path = os.path.splitext([str(frame_info.filename) for frame_info in inspect.stack()][1])[0]
8
+ mid_dir_path = os.path.relpath(caller_dir_path, assets_dir_path).lstrip(os.path.pardir + os.path.sep)
9
+ assets_file_path = os.path.join(assets_dir_path, mid_dir_path, path)
10
+ return assets_file_path
py3_kit/file.py ADDED
@@ -0,0 +1,209 @@
1
+ """
2
+ rar
3
+ https://www.rarlab.com/download.htm
4
+
5
+ 7z
6
+ Windows: https://www.7-zip.org/a/7zr.exe
7
+ Darwin: https://www.7-zip.org/a/7z2501-mac.tar.xz
8
+ Linux: https://www.7-zip.org/a/7z2501-linux-x64.tar.xz
9
+ """
10
+ import os
11
+ import platform
12
+ import subprocess
13
+ import sys
14
+ import zipfile
15
+ from typing import Literal, cast
16
+
17
+ import py3_kit
18
+
19
+ COMPRESS_TYPE = Literal["zip", "rar", "7z"]
20
+ DECOMPRESS_TYPE = Literal["zip", "rar", "7z"]
21
+
22
+
23
+ def compress(
24
+ path: str,
25
+ compress_type: COMPRESS_TYPE | None = None,
26
+ compress_file_path: str | None = None
27
+ ) -> str | None:
28
+ path = os.path.abspath(path)
29
+
30
+ if compress_file_path is not None:
31
+ compress_file_path = os.path.abspath(compress_file_path)
32
+ if compress_type is None:
33
+ compress_type = os.path.splitext(compress_file_path)[-1][1:]
34
+ else:
35
+ if compress_type is None:
36
+ compress_type = "zip"
37
+ compress_file_path = os.path.join(
38
+ os.path.dirname(path), os.path.basename(path) + os.extsep + compress_type
39
+ )
40
+
41
+ if compress_type == "zip":
42
+ with zipfile.ZipFile(compress_file_path, "w", zipfile.ZIP_DEFLATED) as zf:
43
+ if os.path.isdir(path):
44
+ for root, dirs, files in os.walk(path):
45
+ for file in files:
46
+ if (file_path := os.path.join(root, file)) == compress_file_path:
47
+ continue
48
+ rel_file_path = os.path.relpath(file_path, path)
49
+ zf.write(file_path, arcname=rel_file_path)
50
+ else:
51
+ rel_file_path = os.path.basename(path)
52
+ zf.write(path, arcname=rel_file_path)
53
+ return compress_file_path
54
+
55
+ if compress_type == "rar":
56
+ if sys.platform.startswith("win"):
57
+ x = py3_kit.assets.get_assets_file_path(f"win{os.sep}Rar.exe")
58
+ elif sys.platform == "darwin":
59
+ x = py3_kit.assets.get_assets_file_path(f"mac{os.sep}rar")
60
+ elif sys.platform.startswith("linux"):
61
+ x = py3_kit.assets.get_assets_file_path(f"linux{os.sep}rar")
62
+ else:
63
+ return None
64
+ args = [
65
+ x,
66
+ "a",
67
+ "-r",
68
+ "-inul",
69
+ "-ep1",
70
+ compress_file_path
71
+ ]
72
+ if os.path.isdir(path):
73
+ args.append(f"{path}{os.sep}*")
74
+ else:
75
+ args.append(path)
76
+ if subprocess.run(args).returncode == 0:
77
+ return compress_file_path
78
+
79
+ if compress_type == "7z":
80
+ if sys.platform.startswith("win"):
81
+ x = py3_kit.assets.get_assets_file_path(f"win{os.sep}7zr.exe")
82
+ elif sys.platform == "darwin":
83
+ x = py3_kit.assets.get_assets_file_path(f"mac{os.sep}7zz")
84
+ elif sys.platform.startswith("linux"):
85
+ x = py3_kit.assets.get_assets_file_path(f"linux{os.sep}7zz")
86
+ else:
87
+ return None
88
+ args = [
89
+ x,
90
+ "a",
91
+ compress_file_path
92
+ ]
93
+ if os.path.isdir(path):
94
+ args.append(f"{path}{os.sep}*")
95
+ else:
96
+ args.append(path)
97
+ args.extend("-bb0 -bso0 -bsp0 -bse0".split())
98
+ if subprocess.run(args).returncode == 0:
99
+ return compress_file_path
100
+
101
+ return None
102
+
103
+
104
+ def decompress(
105
+ compress_file_path: str,
106
+ path: str | None = None
107
+ ) -> str | None:
108
+ compress_file_path = os.path.abspath(compress_file_path)
109
+ if not os.path.isfile(compress_file_path):
110
+ return None
111
+
112
+ if path is not None:
113
+ path = os.path.abspath(path)
114
+ else:
115
+ path = os.path.dirname(compress_file_path)
116
+ os.makedirs(path, exist_ok=True)
117
+
118
+ with open(compress_file_path, "rb") as file:
119
+ data = file.read(8)
120
+ text = data.hex()
121
+ if text in ("504b030414000000",):
122
+ decompress_type = "zip"
123
+ elif text in ("526172211a070100", "526172211a0700cf"):
124
+ decompress_type = "rar"
125
+ elif text in ('377abcaf271c0004',):
126
+ decompress_type = "7z"
127
+ else:
128
+ return None
129
+
130
+ if decompress_type == "zip":
131
+ with zipfile.ZipFile(compress_file_path, "r") as zf:
132
+ zf.extractall(path)
133
+ return path
134
+
135
+ if decompress_type == "rar":
136
+ if sys.platform.startswith("win"):
137
+ x = py3_kit.assets.get_assets_file_path(f"win{os.sep}UnRAR.exe")
138
+ elif sys.platform == "darwin":
139
+ x = py3_kit.assets.get_assets_file_path(f"mac{os.sep}unrar")
140
+ elif sys.platform.startswith("linux"):
141
+ x = py3_kit.assets.get_assets_file_path(f"linux{os.sep}unrar")
142
+ else:
143
+ return None
144
+ args = [
145
+ x,
146
+ "x",
147
+ "-o+",
148
+ "-inul",
149
+ compress_file_path,
150
+ path
151
+ ]
152
+ if subprocess.run(args).returncode == 0:
153
+ return path
154
+
155
+ if decompress_type == "7z":
156
+ if sys.platform.startswith("win"):
157
+ x = py3_kit.assets.get_assets_file_path(f"win{os.sep}7zr.exe")
158
+ elif sys.platform == "darwin":
159
+ x = py3_kit.assets.get_assets_file_path(f"mac{os.sep}7zz")
160
+ elif sys.platform.startswith("linux"):
161
+ x = py3_kit.assets.get_assets_file_path(f"linux{os.sep}7zz")
162
+ else:
163
+ return None
164
+ args = [
165
+ x,
166
+ "x",
167
+ compress_file_path,
168
+ f"-o{path}",
169
+ "-y"
170
+ ]
171
+ args.extend("-bb0 -bso0 -bsp0 -bse0".split())
172
+ if subprocess.run(args).returncode == 0:
173
+ return path
174
+
175
+ return None
176
+
177
+
178
+ def get_file_paths_and_dir_paths(path: str) -> tuple[list[str], list[str]]:
179
+ file_paths = []
180
+ dir_paths = []
181
+
182
+ path = os.path.abspath(path)
183
+ with os.scandir(path) as entries:
184
+ for entry in entries:
185
+ System = Literal["Windows", "Linux", "Darwin"]
186
+ system: System = cast(System, platform.system())
187
+ if system == "Windows":
188
+ from nt import DirEntry
189
+ elif system == "Linux":
190
+ from posix import DirEntry
191
+ elif system == "Darwin":
192
+ from posix import DirEntry
193
+ else:
194
+ raise TypeError(
195
+ f"Invalid type for 'system': "
196
+ f"Expected `Literal[\"Windows\",\"Linux\",\"Darwin\"]`, "
197
+ f"but got {type(system).__name__!r} (value: {system!r})"
198
+ )
199
+
200
+ entry: DirEntry
201
+ if entry.is_file():
202
+ file_paths.append(entry.path)
203
+ elif entry.is_dir():
204
+ dir_paths.append(entry.path)
205
+ sub_file_paths, sub_dir_paths = get_file_paths_and_dir_paths(entry.path)
206
+ file_paths.extend(sub_file_paths)
207
+ dir_paths.extend(sub_dir_paths)
208
+
209
+ return file_paths, dir_paths
py3_kit/image.py ADDED
@@ -0,0 +1,139 @@
1
+ import os
2
+ from tempfile import NamedTemporaryFile
3
+ from typing import Literal
4
+
5
+ import cv2
6
+ import numpy as np
7
+ import pillow_avif # type: ignore
8
+ from PIL import Image
9
+
10
+
11
+ def to_jpg(
12
+ image_file_path: str,
13
+ /,
14
+ jpg_file_path: str | None = None,
15
+ quality: int = 100,
16
+ keep_original: bool = False
17
+ ) -> str | None:
18
+ try:
19
+ with open(image_file_path, "rb") as file:
20
+ prefix_text = file.read(3).hex()
21
+ n = 2
22
+ file.seek(-n, 2)
23
+ suffix_text = file.read(n).hex()
24
+ text = prefix_text + suffix_text
25
+
26
+ image_file_path = os.path.abspath(image_file_path)
27
+ if jpg_file_path is not None:
28
+ jpg_file_path = os.path.abspath(jpg_file_path)
29
+ else:
30
+ jpg_file_path = os.path.splitext(image_file_path)[0] + os.path.extsep + "jpg"
31
+
32
+ if image_file_path == jpg_file_path and text == "ffd8ffffd9":
33
+ return jpg_file_path
34
+
35
+ jpg_dir_path = os.path.dirname(jpg_file_path)
36
+ os.makedirs(jpg_dir_path, exist_ok=True)
37
+
38
+ with Image.open(image_file_path) as image:
39
+ if image.mode in ("RGBA", "LA"):
40
+ background = Image.new("RGB", image.size, (255, 255, 255))
41
+ background.paste(image, mask=image.split()[-1])
42
+ image = background
43
+ elif image.mode == "P":
44
+ image.seek(0) # image.n_frames
45
+ image = image.convert("RGB")
46
+ elif image.mode == "RGB":
47
+ pass
48
+ else:
49
+ return None
50
+
51
+ with NamedTemporaryFile(suffix=os.path.extsep + "jpg", delete=False, dir=jpg_dir_path) as ntf:
52
+ temp_file_path = ntf.name
53
+ image.save(temp_file_path, "JPEG", quality=quality)
54
+ os.replace(temp_file_path, jpg_file_path)
55
+
56
+ if not keep_original:
57
+ from pathlib import Path
58
+ if (
59
+ jpg_file_path != image_file_path and
60
+ (p := Path(image_file_path)).exists() and
61
+ str(p.resolve()) == str(p.absolute())
62
+ ):
63
+ os.remove(image_file_path)
64
+
65
+ return jpg_file_path
66
+ except Exception as e: # noqa
67
+ return None
68
+
69
+
70
+ def resize(image: cv2.typing.MatLike, new_w: int, new_h: int) -> cv2.typing.MatLike:
71
+ return cv2.resize(image, (new_w, new_h), interpolation=cv2.INTER_AREA)
72
+
73
+
74
+ def concat(
75
+ input_image_file_paths: list[str],
76
+ output_image_file_path: str,
77
+ orientation: Literal["vertical", "horizontal"] = "vertical",
78
+ output_image_vertical_width: int | float | None = None,
79
+ output_image_horizontal_height: int | float | None = None
80
+ ) -> bool:
81
+ try:
82
+ input_images = [cv2.imread(i, cv2.IMREAD_UNCHANGED) for i in input_image_file_paths]
83
+
84
+ if any(i is None for i in input_images):
85
+ return False
86
+
87
+ if orientation == "vertical":
88
+ if output_image_vertical_width is None:
89
+ output_image_vertical_width = min(i.shape[1] for i in input_images)
90
+
91
+ resized_images = []
92
+ for i in input_images:
93
+ h, w = i.shape[:2]
94
+ new_w = int(output_image_vertical_width)
95
+ new_h = int(h * new_w / w)
96
+ resized_image = resize(i, new_w, new_h)
97
+ resized_images.append(resized_image)
98
+
99
+ output_image_height = sum(i.shape[0] for i in resized_images)
100
+
101
+ output_image = np.ones((output_image_height, output_image_vertical_width, 3), dtype=np.uint8) * 255
102
+
103
+ y = 0
104
+ for i in resized_images:
105
+ h = i.shape[0]
106
+ output_image[y:y + h, :i.shape[1]] = i
107
+ y += h
108
+
109
+ return cv2.imwrite(output_image_file_path, output_image)
110
+
111
+ elif orientation == "horizontal":
112
+ if output_image_horizontal_height is None:
113
+ output_image_horizontal_height = min(i.shape[0] for i in input_images)
114
+
115
+ resized_images = []
116
+ for i in input_images:
117
+ h, w = i.shape[:2]
118
+ new_h = int(output_image_horizontal_height)
119
+ new_w = int(w * new_h / h)
120
+ resized_image = resize(i, new_w, new_h)
121
+ resized_images.append(resized_image)
122
+
123
+ output_image_width = sum(i.shape[1] for i in resized_images)
124
+
125
+ output_image = np.ones((output_image_horizontal_height, output_image_width, 3), dtype=np.uint8) * 255
126
+
127
+ x = 0
128
+ for i in resized_images:
129
+ w = i.shape[1]
130
+ output_image[:i.shape[0], x:x + w] = i
131
+ x += w
132
+
133
+ return cv2.imwrite(output_image_file_path, output_image)
134
+
135
+ else:
136
+ return False
137
+
138
+ except Exception as e: # noqa
139
+ return False
py3_kit/list.py ADDED
@@ -0,0 +1,32 @@
1
+ from typing import Any
2
+
3
+
4
+ def split(
5
+ data: list[Any],
6
+ num_parts: int | None = None,
7
+ part_size: int | None = None
8
+ ) -> list[Any]:
9
+ if not data:
10
+ return []
11
+
12
+ if num_parts:
13
+ avg_size = len(data) // num_parts
14
+ remainder = len(data) % num_parts
15
+
16
+ result = []
17
+ result.extend(
18
+ [data[start:start + avg_size] for start in range(0, len(data) - avg_size - remainder, avg_size)])
19
+ result.append(data[len(data) - avg_size - remainder:])
20
+ return result
21
+
22
+ elif part_size:
23
+ return [data[i:i + part_size] for i in range(0, len(data), part_size)]
24
+
25
+ else:
26
+ raise ValueError(
27
+ f"Either num_parts: {num_parts!r} or part_size: {part_size!r} must be provided"
28
+ )
29
+
30
+
31
+ def flatten(data: list[Any]) -> list[Any]:
32
+ return sum((flatten(x) if isinstance(x, list) else [x] for x in data), [])
py3_kit/pdf.py ADDED
@@ -0,0 +1,20 @@
1
+ from pypdf import PdfWriter
2
+
3
+
4
+ def concat(
5
+ input_pdf_file_paths: list[str],
6
+ output_pdf_file_path: str
7
+ ) -> bool:
8
+ try:
9
+ writer = PdfWriter()
10
+
11
+ for input_pdf_file_path in input_pdf_file_paths:
12
+ writer.append(input_pdf_file_path)
13
+
14
+ with open(output_pdf_file_path, "wb") as f:
15
+ writer.write(f)
16
+
17
+ return True
18
+
19
+ except Exception: # noqa
20
+ return False
py3_kit/project.py ADDED
@@ -0,0 +1,58 @@
1
+ import hashlib
2
+ from typing import Any, Literal
3
+
4
+ ALGO_TYPE = Literal[
5
+ "md5", "sha1", "sha224", "sha256", "sha384", "sha512", "blake2b", "blake2s", "sha3_224", "sha3_256", "sha3_384",
6
+ "sha3_512"
7
+ ]
8
+
9
+
10
+ def gen_data_id(
11
+ *args: Any,
12
+ keys: list | None = None, item: dict | None = None,
13
+ algo_type: ALGO_TYPE = "sha256",
14
+ encoding: str = "utf-8"
15
+ ) -> str:
16
+ """
17
+ >>> gen_data_id("123456")
18
+ '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
19
+
20
+ Args:
21
+ *args:
22
+ keys:
23
+ item:
24
+ algo_type:
25
+ encoding:
26
+
27
+ Returns:
28
+
29
+ """
30
+ m = hashlib.new(algo_type)
31
+ if args:
32
+ values = args
33
+ elif keys is not None and item is not None:
34
+ if isinstance(keys, list) and isinstance(item, dict):
35
+ values = [item[k] for k in keys if k in item]
36
+ else:
37
+ raise TypeError(
38
+ f"Invalid type for 'keys': "
39
+ f"Expected `list | None`, "
40
+ f"but got {type(keys).__name__} (value: {keys!r})\n"
41
+ f"Invalid type for 'item': "
42
+ f"Expected `dict | None`, "
43
+ f"but got {type(item).__name__} (value: {item!r})"
44
+ )
45
+ elif item is not None:
46
+ values = [item[k] for k in sorted(item.keys())]
47
+ else:
48
+ raise ValueError(
49
+ f"Either args: {args!r} or keys: {keys!r} and item: {item!r} or item: {item!r} must be provided"
50
+ )
51
+
52
+ data = list(map(lambda x: str(x), values))
53
+
54
+ for i in data:
55
+ m.update(i.encode(encoding))
56
+
57
+ data_id = m.hexdigest()
58
+ return data_id
py3_kit/sql.py ADDED
@@ -0,0 +1,15 @@
1
+ from typing import Any
2
+
3
+ import sqlparse
4
+
5
+
6
+ def format(sql: str, **kwargs: Any) -> str: # noqa
7
+ if not kwargs:
8
+ kwargs = dict(
9
+ reindent=True,
10
+ keyword_case="upper",
11
+ identifier_case="lower",
12
+ strip_comments=True
13
+ )
14
+ sql = sqlparse.format(sql, **kwargs)
15
+ return sql
py3_kit/validators.py ADDED
@@ -0,0 +1,21 @@
1
+ from typing import Any, TypeGuard
2
+
3
+
4
+ def is_bool(obj: Any) -> TypeGuard[bool]:
5
+ return isinstance(obj, bool)
6
+
7
+
8
+ def is_int(obj: Any) -> TypeGuard[int]:
9
+ return isinstance(obj, int) and not isinstance(obj, bool)
10
+
11
+
12
+ def is_str(val: Any) -> TypeGuard[str]:
13
+ return isinstance(val, str)
14
+
15
+
16
+ def is_list(val: Any) -> TypeGuard[list[Any]]:
17
+ return isinstance(val, list)
18
+
19
+
20
+ def is_list_of[T](val: list[Any], ele_type: type[T]) -> TypeGuard[list[T]]:
21
+ return isinstance(val, list) and all(isinstance(i, ele_type) for i in val)
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.4
2
+ Name: py3-kit
3
+ Version: 0.0.8
4
+ Summary: python3 kit
5
+ License-File: LICENSE
6
+ Author: mathewgeola
7
+ Author-email: mathewgeola@gmail.com
8
+ Requires-Python: >=3.12,<3.15
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Dist: arrow (>=1.4.0,<2.0.0)
14
+ Requires-Dist: opencv-python (>=4.12.0.88,<5.0.0.0)
15
+ Requires-Dist: openpyxl (>=3.1.5,<4.0.0)
16
+ Requires-Dist: oss2 (>=2.19.1,<3.0.0)
17
+ Requires-Dist: pandas (>=2.3.3,<3.0.0)
18
+ Requires-Dist: pillow (>=12.1.0,<13.0.0)
19
+ Requires-Dist: pillow-avif-plugin (>=1.5.2,<2.0.0)
20
+ Requires-Dist: py3-database (>=0.0.1,<0.0.2)
21
+ Requires-Dist: py3-execute (>=0.0.5,<0.0.6)
22
+ Requires-Dist: py3-logger (>=0.0.7,<0.0.8)
23
+ Requires-Dist: py3-web (>=0.0.7,<0.0.8)
24
+ Requires-Dist: pyinstaller (>=6.17.0,<7.0.0)
25
+ Requires-Dist: pypdf (>=6.6.0,<7.0.0)
26
+ Requires-Dist: sqlparse (>=0.5.5,<0.6.0)
27
+ Description-Content-Type: text/markdown
28
+
29
+ # py3-kit
30
+ python3 kit
31
+
@@ -0,0 +1,31 @@
1
+ py3_kit/__datetime.py,sha256=MtkjvQv66TZh3g2ZEg2pFHUwU92oFwNujxkwmZZdsbo,535
2
+ py3_kit/__init__.py,sha256=LvOX65s7uPofdb3rXnCZFT5yfd9oZYrF0qh8RDa1pI0,689
3
+ py3_kit/__py3_execute__/__init__.py,sha256=VzgVjDvegZNaPuoBCr4_C8E7AiJH1IX70K5Qniypj7E,26
4
+ py3_kit/__py3_logger__/__init__.py,sha256=JmgoHRpIoPg0IgMRf-FAw4BCy9_NgbK7GembF_GP9sA,25
5
+ py3_kit/__py3_web__/__init__.py,sha256=yZ9iRIrSy6gGWuOhbIltQQem89XimM4xc6C-cPf1PTA,22
6
+ py3_kit/__string.py,sha256=o-mcgc3NTVJPdtvARkOCX8-8ekUqF_OyTmpyObgbgQU,535
7
+ py3_kit/_math.py,sha256=q19pDrndfysgAnU8KisgBUyr6SjiajV9j6v6Lbs1sFE,1030
8
+ py3_kit/_pandas.py,sha256=EXxLPcgcxAB87EsPo07ysEYQh0cbUR2Bo0ByhnfhsQ8,764
9
+ py3_kit/_types.py,sha256=ran8qyJDCf8gdwrZtDVDrQ8GFj5YtjEHJB2hQmBxV_Q,1561
10
+ py3_kit/assets/file/linux/7zz,sha256=oYYP3w1uw5Xg4nflIi6apIh0fbSqXIfR7IeaCRa6Cy8,2878000
11
+ py3_kit/assets/file/linux/7zzs,sha256=XvVvvvRKm2GANqkAOdSnCaGOCVDXk5ESEU9cQ2dMVCI,3759224
12
+ py3_kit/assets/file/linux/rar,sha256=ydystOHpMTvhHjcK23kbBjAt0LMUD63UBuY8V1gmPsU,798760
13
+ py3_kit/assets/file/linux/unrar,sha256=9BC4PJQvBkecCqIoIegsXLSErC1rP0FoR8LwdLSPFCM,441632
14
+ py3_kit/assets/file/mac/7zz,sha256=XC_TbwCmb3eH3PG63Zd9RKArUAY_5WeOHxn_ZHl0Mu0,5885696
15
+ py3_kit/assets/file/mac/rar,sha256=V6NgbJLR1mmLWWpeg_z77nFdfnLF-IR_kFAe2vP_XqE,693296
16
+ py3_kit/assets/file/mac/unrar,sha256=kR1GMUZ-Y-GoB2Q_YSs_-x-HrpHT1P5ISK9KhuVk4Qc,393992
17
+ py3_kit/assets/file/win/7zr.exe,sha256=J8vj1YBK0J6Qu8qpFtoNXDsL6UYtDg-2y1S-XtkDCHU,601088
18
+ py3_kit/assets/file/win/Rar.exe,sha256=cgpIHEQSHKuHDC5sVIMR2Qkt2X9nwpSVFoltI0vlbx4,822480
19
+ py3_kit/assets/file/win/UnRAR.exe,sha256=i8LtOnNL6ekMY_Os9q3Pbh8eZN28ZkPOIZDQQB2Y77U,548560
20
+ py3_kit/assets.py,sha256=WrrsPhrqX1i6lGVdbm_1Xgvvj-ViBbyWPJVmn7GFFRI,454
21
+ py3_kit/file.py,sha256=_xBsDqEB6imYg-P5dKRkgilFiRf9rtkWr4cFjAA9bQk,6765
22
+ py3_kit/image.py,sha256=TnDZTt-nTQV0NSqzWlAOTOLXMLRErDBkmdzJOfr9i1U,4809
23
+ py3_kit/list.py,sha256=ZBrvUsFcBIF41Juy_qInwuyZOOL8m4snXSLDS3pPe-A,892
24
+ py3_kit/pdf.py,sha256=zWrhf09N8DwbHOVBAzFqzW5voCXzDIKXcNEmHMxVFcw,425
25
+ py3_kit/project.py,sha256=tCwV6m3JZUmorBFW2kxVm5w4DWj_lUaMISjXMnhRT0Q,1604
26
+ py3_kit/sql.py,sha256=z6I7-AHuxJo6QeRrK4L7lnrT_VOn9tAwoquy-vDgAnY,332
27
+ py3_kit/validators.py,sha256=SWGVUv8ACrAMihVjVM7hSjTEQA0rNV3ScPFHsvQO3CY,528
28
+ py3_kit-0.0.8.dist-info/METADATA,sha256=5b7Ol89ZI5xao6uTtCG4uu2_SxqnRXBO65UCDcmAnM8,1041
29
+ py3_kit-0.0.8.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
30
+ py3_kit-0.0.8.dist-info/licenses/LICENSE,sha256=l9yqPB-E89lQPmUwybOix3aliW6hox6ZY14E0CnXK6c,1068
31
+ py3_kit-0.0.8.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mathewgeola
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.