pykitool 0.0.1__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.
- pykitool/__init__.py +0 -0
- pykitool/base/__init__.py +0 -0
- pykitool/base/cache.py +352 -0
- pykitool/base/enums.py +102 -0
- pykitool/base/exception.py +6 -0
- pykitool/base/response.py +82 -0
- pykitool/base/tlog.py +230 -0
- pykitool/sqliter/__init__.py +0 -0
- pykitool/sqliter/exception.py +30 -0
- pykitool/sqliter/middleware.py +84 -0
- pykitool/sqliter/plus.py +125 -0
- pykitool/sqliter/repo.py +188 -0
- pykitool/utils/__init__.py +0 -0
- pykitool/utils/cbfile.py +697 -0
- pykitool/utils/cbrequest.py +473 -0
- pykitool/utils/cbruntime.py +870 -0
- pykitool/utils/cbutils.py +518 -0
- pykitool-0.0.1.dist-info/METADATA +36 -0
- pykitool-0.0.1.dist-info/RECORD +21 -0
- pykitool-0.0.1.dist-info/WHEEL +5 -0
- pykitool-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import random
|
|
7
|
+
import re
|
|
8
|
+
import secrets
|
|
9
|
+
import string
|
|
10
|
+
import subprocess
|
|
11
|
+
import time
|
|
12
|
+
import uuid
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, List, Optional, Union
|
|
16
|
+
|
|
17
|
+
from loguru import logger
|
|
18
|
+
|
|
19
|
+
from pykitool.base.enums import Platform
|
|
20
|
+
from pykitool.utils import cbruntime
|
|
21
|
+
|
|
22
|
+
# 预编译正则表达式(模块级别)
|
|
23
|
+
_HTTP_PATTERN = re.compile(r"^https?://")
|
|
24
|
+
_NUMBER_PATTERN = re.compile(r"(\d+)")
|
|
25
|
+
|
|
26
|
+
# 获取随机 User-Agent
|
|
27
|
+
_ua_instance = None
|
|
28
|
+
|
|
29
|
+
# ================================ Common 通用功能 ================================
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# 判断是否为调试模式
|
|
33
|
+
def is_debug() -> bool:
|
|
34
|
+
return cbruntime.get_arg(["--reload", "--debug"], False)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# 判断是否为 HTTP/HTTPS 链接
|
|
38
|
+
def is_http(url: str) -> bool:
|
|
39
|
+
if not url:
|
|
40
|
+
return False
|
|
41
|
+
return _HTTP_PATTERN.match(url) is not None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ================================ Device 设备信息 ================================
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# 判断是否为 Windows 系统
|
|
48
|
+
def is_windows() -> bool:
|
|
49
|
+
return platform.system().lower() == Platform.Window.value
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# 判断是否为 Linux 系统
|
|
53
|
+
def is_linux() -> bool:
|
|
54
|
+
return platform.system().lower() == Platform.Linux.value
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# 判断是否为 Mac 系统
|
|
58
|
+
def is_mac() -> bool:
|
|
59
|
+
return platform.system().lower() == Platform.Mac.value
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# 生成指定范围内的随机数
|
|
63
|
+
def get_random(start: int = 10000000, end: int = 99999999) -> int:
|
|
64
|
+
return random.randint(start, end)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ================================ Git 版本信息 ================================
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_git_info():
|
|
71
|
+
|
|
72
|
+
git_info = {"branch": "unknown", "date": "unknown", "hash": "unknown"}
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
repo_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
|
76
|
+
|
|
77
|
+
# 获取分支名
|
|
78
|
+
result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=repo_path, capture_output=True, text=True, timeout=5)
|
|
79
|
+
if result.returncode == 0:
|
|
80
|
+
git_info["branch"] = result.stdout.strip()
|
|
81
|
+
|
|
82
|
+
# 获取最后提交时间
|
|
83
|
+
result = subprocess.run(["git", "log", "-1", "--format=%ci"], cwd=repo_path, capture_output=True, text=True, timeout=5)
|
|
84
|
+
if result.returncode == 0:
|
|
85
|
+
git_info["date"] = result.stdout.strip()
|
|
86
|
+
|
|
87
|
+
# 获取短 commit hash
|
|
88
|
+
result = subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=repo_path, capture_output=True, text=True, timeout=5)
|
|
89
|
+
if result.returncode == 0:
|
|
90
|
+
git_info["hash"] = result.stdout.strip()
|
|
91
|
+
except Exception as e:
|
|
92
|
+
print(f"Warning: Failed to get git info - {e}")
|
|
93
|
+
|
|
94
|
+
return git_info
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ================================ Json 数据操作 ================================
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# 验证字符串是否为有效的 JSON 格式
|
|
101
|
+
def is_json(data: str) -> bool:
|
|
102
|
+
if not data or not isinstance(data, str):
|
|
103
|
+
return False
|
|
104
|
+
# 快速检查:JSON 必须以 {、[ 开头或是基本类型
|
|
105
|
+
data_stripped = data.strip()
|
|
106
|
+
if not data_stripped:
|
|
107
|
+
return False
|
|
108
|
+
first_char = data_stripped[0]
|
|
109
|
+
if first_char not in ("{", "[", '"', "t", "f", "n", "-", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"):
|
|
110
|
+
return False
|
|
111
|
+
try:
|
|
112
|
+
json.loads(data)
|
|
113
|
+
return True
|
|
114
|
+
except (ValueError, TypeError):
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# 解析 JSON 字符串
|
|
119
|
+
def load_json(data: str, default: Any = None) -> Any:
|
|
120
|
+
try:
|
|
121
|
+
return json.loads(data)
|
|
122
|
+
except (ValueError, TypeError) as e:
|
|
123
|
+
logger.error(f"Failed to parse JSON: {str(e)}")
|
|
124
|
+
return default
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# 从文件加载 JSON 数据
|
|
128
|
+
def load_json_file(path: Union[str, Path], default: Any = None) -> Any:
|
|
129
|
+
try:
|
|
130
|
+
path_obj = Path(path) if isinstance(path, str) else path
|
|
131
|
+
if not path_obj.exists():
|
|
132
|
+
return default
|
|
133
|
+
with open(path_obj, "r", encoding="utf-8") as file:
|
|
134
|
+
return json.load(file)
|
|
135
|
+
except (ValueError, IOError, OSError) as e:
|
|
136
|
+
logger.error(f"Failed to load JSON file {path}: {str(e)}")
|
|
137
|
+
return default
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# 将数据转换为 JSON 字符串(紧凑格式)
|
|
141
|
+
def to_json(data: Any) -> str:
|
|
142
|
+
return json.dumps(
|
|
143
|
+
data,
|
|
144
|
+
ensure_ascii=False,
|
|
145
|
+
separators=(",", ":"),
|
|
146
|
+
default=lambda o: o.model_dump() if hasattr(o, "model_dump") else str(o),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# 将数据转换为格式化的 JSON 字符串
|
|
151
|
+
def to_json_pretty(data: Any, indent: int = 4, sort_keys: bool = False) -> str:
|
|
152
|
+
return json.dumps(
|
|
153
|
+
data,
|
|
154
|
+
ensure_ascii=False,
|
|
155
|
+
default=lambda o: o.model_dump() if hasattr(o, "model_dump") else str(o),
|
|
156
|
+
indent=indent,
|
|
157
|
+
sort_keys=sort_keys,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ================================ Encrypt 加密解密 ================================
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# 加密数据
|
|
165
|
+
def encrypt(data: Any, password: str = "0123456789") -> Optional[str]:
|
|
166
|
+
try:
|
|
167
|
+
raw = json.dumps(data).encode("utf-8")
|
|
168
|
+
key = hashlib.sha256(password.encode()).digest()
|
|
169
|
+
encrypted = bytes([b ^ key[i % len(key)] for i, b in enumerate(raw)])
|
|
170
|
+
return base64.b64encode(encrypted).decode("utf-8")
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.error(f"Encryption failed: {str(e)}")
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# 解密数据
|
|
177
|
+
def decrypt(enc_data: str, password: str = "0123456789") -> Any:
|
|
178
|
+
try:
|
|
179
|
+
encrypted = base64.b64decode(enc_data)
|
|
180
|
+
key = hashlib.sha256(password.encode()).digest()
|
|
181
|
+
decrypted = bytes([b ^ key[i % len(key)] for i, b in enumerate(encrypted)])
|
|
182
|
+
return json.loads(decrypted.decode("utf-8"))
|
|
183
|
+
except Exception as e:
|
|
184
|
+
logger.error(f"Decryption failed: {str(e)}")
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ================================ String 字符串操作 ================================
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# 生成终端超链接
|
|
192
|
+
def str_hyperlink(url: str, text: str) -> str:
|
|
193
|
+
ESC = "\033"
|
|
194
|
+
return f"{ESC}]8;;{url}{ESC}\\{text}{ESC}]8;;{ESC}\\"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# 生成随机密码
|
|
198
|
+
def str_password(length: int = 6) -> str:
|
|
199
|
+
chars = string.ascii_letters + string.digits # 字母+数字
|
|
200
|
+
while True:
|
|
201
|
+
password = "".join(secrets.choice(chars) for _ in range(length))
|
|
202
|
+
# 确保至少包含一个字母和一个数字
|
|
203
|
+
if any(c.isdigit() for c in password) and any(c.isalpha() for c in password):
|
|
204
|
+
return password
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# 计算字符串的 MD5 哈希值
|
|
208
|
+
def str_to_md5(text: str) -> str:
|
|
209
|
+
md5_hash = hashlib.md5()
|
|
210
|
+
md5_hash.update(text.encode("utf-8"))
|
|
211
|
+
return md5_hash.hexdigest()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# 计算字符串的 MD5 哈希值(截取指定位数)
|
|
215
|
+
def str_to_md5_short(text: str, length: int = 6) -> str:
|
|
216
|
+
return str_to_md5(text)[:length]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# 生成 UUID 字符串(无连字符)
|
|
220
|
+
def uuid_str() -> str:
|
|
221
|
+
return str(uuid.uuid4()).replace("-", "")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# 生成数字型 UUID(时间戳+随机数)
|
|
225
|
+
def uuid_numeric() -> str:
|
|
226
|
+
return str(int(time.time() * 1000)) + str(random.randint(1000, 9999))
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# 生成随机 User-Agent
|
|
230
|
+
def random_ua() -> str:
|
|
231
|
+
global _ua_instance
|
|
232
|
+
if _ua_instance is None:
|
|
233
|
+
from fake_useragent import UserAgent
|
|
234
|
+
|
|
235
|
+
_ua_instance = UserAgent()
|
|
236
|
+
return _ua_instance.random
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# 生成指定范围内的随机数
|
|
240
|
+
def random_int(start: int = 10000000, end: int = 99999999) -> int:
|
|
241
|
+
return random.randint(start, end)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# ================================ Time 时间操作 ================================
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
# 获取当前时间
|
|
248
|
+
def get_current_time(format: str = "%Y-%m-%d %H:%M:%S", as_string: bool = True) -> Union[str, datetime]:
|
|
249
|
+
now = datetime.now()
|
|
250
|
+
if as_string:
|
|
251
|
+
return now.strftime(format)
|
|
252
|
+
return now
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# 将时间戳格式化为本地时间字符串
|
|
256
|
+
def format_to_localtime(ts: Union[int, float] = 0, format: str = "%Y-%m-%d %H:%M:%S") -> Union[str, int]:
|
|
257
|
+
try:
|
|
258
|
+
if ts == 0:
|
|
259
|
+
return "Never"
|
|
260
|
+
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts))
|
|
261
|
+
except Exception as e:
|
|
262
|
+
logger.error(f"Format to localtime failed: {str(e)}")
|
|
263
|
+
return 0
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# 计算距离指定时间的剩余天数
|
|
267
|
+
def time_interval_days(end_time: str, now_time: Optional[str] = None, use_http: bool = False, url: str = "https://www.baidu.com") -> int:
|
|
268
|
+
fmt = "%Y-%m-%d %H:%M:%S"
|
|
269
|
+
end_dt = datetime.strptime(end_time, fmt)
|
|
270
|
+
|
|
271
|
+
if now_time:
|
|
272
|
+
now_dt = datetime.strptime(now_time, fmt)
|
|
273
|
+
else:
|
|
274
|
+
now_dt = datetime.now()
|
|
275
|
+
|
|
276
|
+
delta = end_dt - now_dt
|
|
277
|
+
return delta.days if delta.days >= 0 else -1
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# 将 cron 表达式的小时字段从 CST(东八区)转换为 UTC(-8h),仅处理固定数字小时
|
|
281
|
+
def cst_to_utc(expr: str) -> str:
|
|
282
|
+
parts = expr.strip().split()
|
|
283
|
+
if len(parts) != 5:
|
|
284
|
+
return expr
|
|
285
|
+
minute, hour, day, month, weekday = parts
|
|
286
|
+
if hour.isdigit():
|
|
287
|
+
utc_hour = (int(hour) - 8) % 24
|
|
288
|
+
return " ".join([minute, str(utc_hour), day, month, weekday])
|
|
289
|
+
return expr
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# ================================ List 列表操作 ================================
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# 根据字段值查找列表中的元素
|
|
296
|
+
def find_list_item_by_field(items: List[Union[Dict[str, Any], Any]], field: str, value: Any) -> Optional[Union[Dict[str, Any], Any]]:
|
|
297
|
+
for item in items:
|
|
298
|
+
if isinstance(item, dict):
|
|
299
|
+
if item.get(field) == value:
|
|
300
|
+
return item
|
|
301
|
+
else:
|
|
302
|
+
if getattr(item, field, None) == value:
|
|
303
|
+
return item
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# 查找字段值在指定集合中的所有元素
|
|
308
|
+
def find_list_item_by_value_in_set(array: List[Dict[str, Any]], field: str, values_set: set) -> List[Dict[str, Any]]:
|
|
309
|
+
return [item for item in array if item.get(field) in values_set]
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# 对字符串列表排序(数字优先,字母其次)
|
|
313
|
+
def sort_by_array(items: List[str]) -> List[str]:
|
|
314
|
+
def sort_key(x: str) -> tuple[Union[int, float], str]:
|
|
315
|
+
match = _NUMBER_PATTERN.match(x)
|
|
316
|
+
num = int(match.group(1)) if match else float("inf")
|
|
317
|
+
return (num, x.lower())
|
|
318
|
+
|
|
319
|
+
return sorted(items, key=sort_key)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# 对字典按键排序(数字优先,字母其次)
|
|
323
|
+
def sort_by_dict(d: Dict[str, Any]) -> Dict[str, Any]:
|
|
324
|
+
def sort_key(k: str) -> tuple[Union[int, float], str]:
|
|
325
|
+
match = _NUMBER_PATTERN.match(k)
|
|
326
|
+
num = int(match.group(1)) if match else float("inf")
|
|
327
|
+
return (num, k.lower())
|
|
328
|
+
|
|
329
|
+
sorted_items = sorted(d.items(), key=lambda item: sort_key(item[0]))
|
|
330
|
+
return dict(sorted_items)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ================================ 调用示例 ================================
|
|
334
|
+
|
|
335
|
+
if __name__ == "__main__":
|
|
336
|
+
|
|
337
|
+
# ==================== Common 通用功能示例 ====================
|
|
338
|
+
|
|
339
|
+
# 判断是否为调试模式
|
|
340
|
+
# result = is_debug()
|
|
341
|
+
# logger.info(f"Is debug mode: {result}")
|
|
342
|
+
|
|
343
|
+
# 判断是否为 HTTP/HTTPS 链接
|
|
344
|
+
# result = is_http("https://example.com")
|
|
345
|
+
# logger.info(f"Is HTTP URL: {result}")
|
|
346
|
+
|
|
347
|
+
# ==================== Network 网络与语言示例 ====================
|
|
348
|
+
|
|
349
|
+
# 获取系统语言环境
|
|
350
|
+
# result = get_locale()
|
|
351
|
+
# logger.info(f"System locale: {result}")
|
|
352
|
+
|
|
353
|
+
# 根据语言代码获取完整的语言环境标识
|
|
354
|
+
# result = get_locale_by_lang("zh", "zh-CN")
|
|
355
|
+
# logger.info(f"Locale by lang: {result}")
|
|
356
|
+
|
|
357
|
+
# 解析语言标签,返回语言和地区
|
|
358
|
+
# lang, region = parse_locale("zh-CN")
|
|
359
|
+
# logger.info(f"Parse locale: lang={lang}, region={region}")
|
|
360
|
+
|
|
361
|
+
# 检测文本的语言类型
|
|
362
|
+
# result = detect_language("Hello World")
|
|
363
|
+
# logger.info(f"Detect language: {result}")
|
|
364
|
+
|
|
365
|
+
# 智能拼接文本(根据语言决定是否添加空格)
|
|
366
|
+
# result = smart_join(["Hello", "World"])
|
|
367
|
+
# logger.info(f"Smart join: {result}")
|
|
368
|
+
|
|
369
|
+
# ==================== Device 设备信息示例 ====================
|
|
370
|
+
|
|
371
|
+
# 判断操作系统类型
|
|
372
|
+
# logger.info(f"Is Windows: {is_windows()}")
|
|
373
|
+
# logger.info(f"Is Linux: {is_linux()}")
|
|
374
|
+
# logger.info(f"Is Mac: {is_mac()}")
|
|
375
|
+
|
|
376
|
+
# 生成指定范围内的随机数
|
|
377
|
+
# result = get_random(1, 100)
|
|
378
|
+
# logger.info(f"Random number: {result}")
|
|
379
|
+
|
|
380
|
+
# 设置随机种子,确保结果可复现
|
|
381
|
+
# seed = set_random_seed(42)
|
|
382
|
+
# logger.info(f"Set random seed: {seed}")
|
|
383
|
+
|
|
384
|
+
# 获取可用的 CPU 核心数
|
|
385
|
+
# count = cpu_available_count()
|
|
386
|
+
# logger.info(f"Available CPU count: {count}")
|
|
387
|
+
|
|
388
|
+
# 判断 CUDA 是否可用
|
|
389
|
+
# result = cuda_is_available()
|
|
390
|
+
# logger.info(f"CUDA available: {result}")
|
|
391
|
+
|
|
392
|
+
# 清空 CUDA 显存缓存
|
|
393
|
+
# cuda_memory_clear()
|
|
394
|
+
# logger.info("CUDA memory cleared")
|
|
395
|
+
|
|
396
|
+
# 获取计算设备(CPU 或 GPU)
|
|
397
|
+
# result = device()
|
|
398
|
+
# logger.info(f"Device: {result}")
|
|
399
|
+
|
|
400
|
+
# ==================== Json JSON操作示例 ====================
|
|
401
|
+
|
|
402
|
+
# 验证字符串是否为有效的 JSON 格式
|
|
403
|
+
# result = is_json('{"key": "value"}')
|
|
404
|
+
# logger.info(f"Is JSON: {result}")
|
|
405
|
+
|
|
406
|
+
# 解析 JSON 字符串
|
|
407
|
+
# result = load_json('{"key": "value"}')
|
|
408
|
+
# logger.info(f"Load JSON: {result}")
|
|
409
|
+
|
|
410
|
+
# 从文件加载 JSON 数据
|
|
411
|
+
# result = load_json_file("config.json", default={})
|
|
412
|
+
# logger.info(f"Load JSON file: {result}")
|
|
413
|
+
|
|
414
|
+
# 将数据转换为 JSON 字符串(紧凑格式)
|
|
415
|
+
# data = {"name": "张三", "age": 30, "city": "北京"}
|
|
416
|
+
# result = to_json(data)
|
|
417
|
+
# logger.info(f"To JSON: {result}")
|
|
418
|
+
|
|
419
|
+
# 将数据转换为格式化的 JSON 字符串
|
|
420
|
+
# result = to_json_pretty(data, indent=2)
|
|
421
|
+
# logger.info(f"To JSON pretty:\n{result}")
|
|
422
|
+
|
|
423
|
+
# ==================== Encrypt 加密解密示例 ====================
|
|
424
|
+
|
|
425
|
+
# 加密和解密数据
|
|
426
|
+
# original_data = {"username": "admin", "password": "123456"}
|
|
427
|
+
# encrypted = encrypt(original_data)
|
|
428
|
+
# logger.info(f"Encrypted: {encrypted}")
|
|
429
|
+
# decrypted = decrypt(encrypted)
|
|
430
|
+
# logger.info(f"Decrypted: {decrypted}")
|
|
431
|
+
|
|
432
|
+
# ==================== String 字符串操作示例 ====================
|
|
433
|
+
|
|
434
|
+
# 生成终端超链接
|
|
435
|
+
# result = str_hyperlink("https://github.com", "GitHub")
|
|
436
|
+
# logger.info(f"Hyperlink: {result}")
|
|
437
|
+
|
|
438
|
+
# 生成随机密码
|
|
439
|
+
# result = str_password(8)
|
|
440
|
+
# logger.info(f"Random password: {result}")
|
|
441
|
+
|
|
442
|
+
# 计算字符串的 MD5 哈希值
|
|
443
|
+
# result = str_to_md5("Hello World")
|
|
444
|
+
# logger.info(f"MD5: {result}")
|
|
445
|
+
|
|
446
|
+
# 计算字符串的 MD5 哈希值(截取指定位数)
|
|
447
|
+
# result = str_to_md5_short("Hello World", 8)
|
|
448
|
+
# logger.info(f"MD5 short: {result}")
|
|
449
|
+
|
|
450
|
+
# 生成 UUID 字符串
|
|
451
|
+
# result = uuid_str()
|
|
452
|
+
# logger.info(f"UUID string: {result}")
|
|
453
|
+
|
|
454
|
+
# 生成数字型 UUID
|
|
455
|
+
# result = uuid_numeric()
|
|
456
|
+
# logger.info(f"UUID numeric: {result}")
|
|
457
|
+
|
|
458
|
+
# 生成随机 User-Agent
|
|
459
|
+
# result = random_ua()
|
|
460
|
+
# logger.info(f"Random UA: {result}")
|
|
461
|
+
|
|
462
|
+
# ==================== Time 时间操作示例 ====================
|
|
463
|
+
|
|
464
|
+
# 获取当前时间
|
|
465
|
+
# result = get_current_time()
|
|
466
|
+
# logger.info(f"Current time: {result}")
|
|
467
|
+
|
|
468
|
+
# 获取当前时间(datetime 对象)
|
|
469
|
+
# result = get_current_time(as_string=False)
|
|
470
|
+
# logger.info(f"Current time (datetime): {result}")
|
|
471
|
+
|
|
472
|
+
# 通过 HTTP 请求获取网络时间
|
|
473
|
+
# result = get_http_time()
|
|
474
|
+
# logger.info(f"HTTP time: {result}")
|
|
475
|
+
|
|
476
|
+
# 将时间戳格式化为本地时间字符串
|
|
477
|
+
# result = format_to_localtime(1704067200)
|
|
478
|
+
# logger.info(f"Format timestamp: {result}")
|
|
479
|
+
|
|
480
|
+
# 格式化为 SRT 字幕时间格式
|
|
481
|
+
# result = format_to_srttime(125.5, "s")
|
|
482
|
+
# logger.info(f"SRT time: {result}")
|
|
483
|
+
|
|
484
|
+
# 将 SRT 时间格式转换为秒数
|
|
485
|
+
# result = time_to_ss("00:00:14,420")
|
|
486
|
+
# logger.info(f"SRT to seconds: {result}")
|
|
487
|
+
|
|
488
|
+
# 将 SRT 时间格式转换为毫秒数
|
|
489
|
+
# result = time_to_ms("00:00:14,420")
|
|
490
|
+
# logger.info(f"SRT to milliseconds: {result}")
|
|
491
|
+
|
|
492
|
+
# 计算距离指定时间的剩余天数
|
|
493
|
+
# end_time = "2025-12-31 23:59:59"
|
|
494
|
+
# result = time_interval_days(end_time)
|
|
495
|
+
# logger.info(f"Days until end: {result}")
|
|
496
|
+
|
|
497
|
+
# ==================== List 列表操作示例 ====================
|
|
498
|
+
|
|
499
|
+
# 根据字段值查找列表中的元素
|
|
500
|
+
# items = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
|
|
501
|
+
# result = find_list_item_by_field(items, "id", 2)
|
|
502
|
+
# logger.info(f"Find by field: {result}")
|
|
503
|
+
|
|
504
|
+
# 查找字段值在指定集合中的所有元素
|
|
505
|
+
# result = find_list_item_by_value_in_set(items, "id", {1, 3})
|
|
506
|
+
# logger.info(f"Find by value in set: {result}")
|
|
507
|
+
|
|
508
|
+
# 对字符串列表排序(数字优先,字母其次)
|
|
509
|
+
# arr = ["item10", "item2", "item1", "itemA", "item20"]
|
|
510
|
+
# result = sort_by_array(arr)
|
|
511
|
+
# logger.info(f"Sort array: {result}")
|
|
512
|
+
|
|
513
|
+
# 对字典按键排序(数字优先,字母其次)
|
|
514
|
+
# d = {"key10": "v10", "key2": "v2", "key1": "v1", "keyA": "vA"}
|
|
515
|
+
# result = sort_by_dict(d)
|
|
516
|
+
# logger.info(f"Sort dict: {result}")
|
|
517
|
+
|
|
518
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pykitool
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Author: xiesx
|
|
5
|
+
Project-URL: Homepage, https://github.com/xiesx123/pykitool
|
|
6
|
+
Project-URL: Documentation, https://wgp520.github.io/hutool-python
|
|
7
|
+
Project-URL: Repository, https://github.com/xiesx123/pykitool
|
|
8
|
+
Project-URL: Issues, https://github.com/xiesx123/pykitool/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/xiesx123/pykitool/blob/master/docs/changelog.md
|
|
10
|
+
Keywords: toolkit,tools,utils,helper
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: asyncio==4.0.0
|
|
25
|
+
Requires-Dist: fake-useragent>=2.0.0
|
|
26
|
+
Requires-Dist: hutool-python>=1.1.1
|
|
27
|
+
Requires-Dist: loguru>=0.7.3
|
|
28
|
+
Requires-Dist: psutil>=7.2.2
|
|
29
|
+
Requires-Dist: pydantic>=2.10.6
|
|
30
|
+
Requires-Dist: requests-cache>=1.3.2
|
|
31
|
+
Requires-Dist: sqlalchemy>=2.0.51
|
|
32
|
+
Requires-Dist: sqlmodel>=0.0.29
|
|
33
|
+
Requires-Dist: starlette>=0.44.0
|
|
34
|
+
Requires-Dist: tqdm>=4.68.3
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
pykitool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
pykitool/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
pykitool/base/cache.py,sha256=BAld3v7dlidzco63DE6C7lGrMxWj4_s4rxWUt-LIYAU,12049
|
|
4
|
+
pykitool/base/enums.py,sha256=Qtvjqw7Mgrn7z8zZCz2l08XMVRI3AmNSR4K6nOZ4lGE,2689
|
|
5
|
+
pykitool/base/exception.py,sha256=HbWc78b3BlGUMPQpXNf09tqJSuurv4Tth1hYOJ3u2kU,212
|
|
6
|
+
pykitool/base/response.py,sha256=uRiEMMO7q-jyqWhauskBkqLdmCvsio4JM-bM4ztAlq8,2449
|
|
7
|
+
pykitool/base/tlog.py,sha256=LVndIXawjvlaaHy8q3IMmjveJ8QRPgucD2c62I-QTK4,6831
|
|
8
|
+
pykitool/sqliter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
pykitool/sqliter/exception.py,sha256=JBwIcwF_lWyxQYjVVnWoauZTz1QCNrkDgRdoduewep4,896
|
|
10
|
+
pykitool/sqliter/middleware.py,sha256=B8fPvyvtIJI5QZvV4OqwxVxONwzbhBgFHj4OlXOmf4g,2915
|
|
11
|
+
pykitool/sqliter/plus.py,sha256=Z4ZQ2vZ7HEuoGe47BJinEmWf8TBBUFr7xMdxkUIBTik,3696
|
|
12
|
+
pykitool/sqliter/repo.py,sha256=EQyX3zgGPajmfFEJEPZVImVDU99SFpes9h5kVJ7g0sw,7660
|
|
13
|
+
pykitool/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
pykitool/utils/cbfile.py,sha256=5EQEgRNlgdgMaSADibYiuGUbl5uBlZPN8pwt3LFpFug,24521
|
|
15
|
+
pykitool/utils/cbrequest.py,sha256=aa0Gh-2xwSqjruhv_x0UwFBh_I2aBCX1B1XpEImJIQA,16008
|
|
16
|
+
pykitool/utils/cbruntime.py,sha256=u7MdawuvKwH4RzrItFQlkmrd0Kp6MgAcbt-C31o2NeI,30659
|
|
17
|
+
pykitool/utils/cbutils.py,sha256=R3ludgfKtLK6t9dG6WXVoVsuf7ePtG-PKYqjKJFHaBQ,16962
|
|
18
|
+
pykitool-0.0.1.dist-info/METADATA,sha256=y9CDFG1ApoTLhpSjOw71wkPQeLJgAsrcYWKyZKW106Q,1526
|
|
19
|
+
pykitool-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
20
|
+
pykitool-0.0.1.dist-info/top_level.txt,sha256=hGytFFLb2K2eleUxmYmrBRH1Db2Qhsh4FgGPfgo5dIQ,9
|
|
21
|
+
pykitool-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pykitool
|