hutool-python 1.0.0__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.
Files changed (89) hide show
  1. hutool/__init__.py +174 -0
  2. hutool/cache/__init__.py +7 -0
  3. hutool/cache/cache_util.py +47 -0
  4. hutool/cache/fifo_cache.py +87 -0
  5. hutool/cache/lfu_cache.py +129 -0
  6. hutool/cache/lru_cache.py +93 -0
  7. hutool/cache/timed_cache.py +115 -0
  8. hutool/captcha/__init__.py +3 -0
  9. hutool/captcha/captcha_util.py +215 -0
  10. hutool/core/__init__.py +23 -0
  11. hutool/core/_base.py +61 -0
  12. hutool/core/bean.py +214 -0
  13. hutool/core/codec.py +111 -0
  14. hutool/core/coll.py +635 -0
  15. hutool/core/date.py +1024 -0
  16. hutool/core/exceptions.py +66 -0
  17. hutool/core/io/__init__.py +0 -0
  18. hutool/core/io/data_size_util.py +79 -0
  19. hutool/core/io/file_name_util.py +111 -0
  20. hutool/core/io/file_util.py +650 -0
  21. hutool/core/io/io_util.py +133 -0
  22. hutool/core/io/path_util.py +247 -0
  23. hutool/core/io/resource_util.py +137 -0
  24. hutool/core/map.py +933 -0
  25. hutool/core/math_util.py +105 -0
  26. hutool/core/net.py +288 -0
  27. hutool/core/text/__init__.py +0 -0
  28. hutool/core/text/csv_util.py +54 -0
  29. hutool/core/text/str_builder.py +224 -0
  30. hutool/core/text/unicode_util.py +58 -0
  31. hutool/core/tree.py +242 -0
  32. hutool/core/util/__init__.py +63 -0
  33. hutool/core/util/array_util.py +503 -0
  34. hutool/core/util/boolean_util.py +124 -0
  35. hutool/core/util/charset_util.py +60 -0
  36. hutool/core/util/class_util.py +136 -0
  37. hutool/core/util/coordinate_util.py +186 -0
  38. hutool/core/util/credit_code_util.py +110 -0
  39. hutool/core/util/desensitized_util.py +194 -0
  40. hutool/core/util/enum_util.py +94 -0
  41. hutool/core/util/escape_util.py +97 -0
  42. hutool/core/util/hash_util.py +243 -0
  43. hutool/core/util/hex_util.py +140 -0
  44. hutool/core/util/id_util.py +147 -0
  45. hutool/core/util/idcard_util.py +300 -0
  46. hutool/core/util/number_util.py +720 -0
  47. hutool/core/util/object_util.py +294 -0
  48. hutool/core/util/page_util.py +61 -0
  49. hutool/core/util/phone_util.py +140 -0
  50. hutool/core/util/random_util.py +112 -0
  51. hutool/core/util/re_util.py +231 -0
  52. hutool/core/util/reflect_util.py +135 -0
  53. hutool/core/util/runtime_util.py +89 -0
  54. hutool/core/util/str_util.py +2320 -0
  55. hutool/core/util/system_util.py +62 -0
  56. hutool/core/util/url_util.py +232 -0
  57. hutool/core/util/version_util.py +41 -0
  58. hutool/core/util/xml_util.py +158 -0
  59. hutool/core/util/zip_util.py +126 -0
  60. hutool/cron/__init__.py +4 -0
  61. hutool/cron/cron_pattern.py +123 -0
  62. hutool/cron/cron_util.py +115 -0
  63. hutool/crypto/__init__.py +5 -0
  64. hutool/crypto/digest_util.py +167 -0
  65. hutool/crypto/secure_util.py +311 -0
  66. hutool/crypto/sign_util.py +74 -0
  67. hutool/dfa/__init__.py +3 -0
  68. hutool/dfa/sensitive_util.py +114 -0
  69. hutool/extra/__init__.py +6 -0
  70. hutool/extra/emoji_util.py +90 -0
  71. hutool/extra/pinyin_util.py +44 -0
  72. hutool/extra/qr_code_util.py +58 -0
  73. hutool/extra/template_util.py +41 -0
  74. hutool/http/__init__.py +6 -0
  75. hutool/http/html_util.py +88 -0
  76. hutool/http/http_request.py +188 -0
  77. hutool/http/http_response.py +139 -0
  78. hutool/http/http_util.py +237 -0
  79. hutool/json_util.py +251 -0
  80. hutool/jwt_util.py +57 -0
  81. hutool/setting/__init__.py +5 -0
  82. hutool/setting/props_util.py +79 -0
  83. hutool/setting/setting_util.py +80 -0
  84. hutool/setting/yaml_util.py +45 -0
  85. hutool_python-1.0.0.dist-info/LICENSE +127 -0
  86. hutool_python-1.0.0.dist-info/METADATA +438 -0
  87. hutool_python-1.0.0.dist-info/RECORD +89 -0
  88. hutool_python-1.0.0.dist-info/WHEEL +5 -0
  89. hutool_python-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,237 @@
1
+ import re
2
+ from typing import Dict, List, Optional
3
+ from urllib.parse import parse_qs, quote, unquote
4
+
5
+ from .http_request import HttpRequest
6
+
7
+
8
+ class HttpUtil:
9
+ """HTTP工具类,提供常用的HTTP操作方法"""
10
+
11
+ @staticmethod
12
+ def is_https(url: str) -> bool:
13
+ """判断URL是否为HTTPS协议
14
+
15
+ :param url: 待判断的URL字符串
16
+ :return: 如果URL以 https:// 开头返回 True,否则返回 False
17
+ """
18
+ return url is not None and url.lower().startswith("https://")
19
+
20
+ @staticmethod
21
+ def is_http(url: str) -> bool:
22
+ """判断URL是否为HTTP协议
23
+
24
+ :param url: 待判断的URL字符串
25
+ :return: 如果URL以 http:// 开头返回 True,否则返回 False
26
+ """
27
+ return url is not None and url.lower().startswith("http://")
28
+
29
+ @staticmethod
30
+ def get(
31
+ url: str,
32
+ params: Optional[dict] = None,
33
+ timeout: int = 30000,
34
+ headers: Optional[dict] = None,
35
+ ) -> str:
36
+ """发送GET请求并返回响应体字符串
37
+
38
+ :param url: 请求URL
39
+ :param params: 查询参数
40
+ :param timeout: 超时时间(毫秒)
41
+ :param headers: 请求头
42
+ :return: 响应体字符串
43
+ """
44
+ request = HttpRequest.get(url).timeout(timeout)
45
+ if headers:
46
+ request.headers(headers)
47
+ if params:
48
+ request._params = params
49
+ response = request.execute()
50
+ return response.to_str()
51
+
52
+ @staticmethod
53
+ def post(
54
+ url: str,
55
+ data=None,
56
+ json_data=None,
57
+ timeout: int = 30000,
58
+ headers: Optional[dict] = None,
59
+ ) -> str:
60
+ """发送POST请求并返回响应体字符串
61
+
62
+ :param url: 请求URL
63
+ :param data: 表单数据
64
+ :param json_data: JSON数据
65
+ :param timeout: 超时时间(毫秒)
66
+ :param headers: 请求头
67
+ :return: 响应体字符串
68
+ """
69
+ request = HttpRequest.post(url).timeout(timeout)
70
+ if headers:
71
+ request.headers(headers)
72
+ if data is not None:
73
+ if isinstance(data, dict):
74
+ for k, v in data.items():
75
+ request.form(k, str(v))
76
+ else:
77
+ request.body(str(data))
78
+ if json_data is not None:
79
+ request.json(json_data)
80
+ response = request.execute()
81
+ return response.to_str()
82
+
83
+ @staticmethod
84
+ def create_get(url: str) -> HttpRequest:
85
+ """创建GET请求对象
86
+
87
+ :param url: 请求URL
88
+ :return: HttpRequest对象
89
+ """
90
+ return HttpRequest.get(url)
91
+
92
+ @staticmethod
93
+ def create_post(url: str) -> HttpRequest:
94
+ """创建POST请求对象
95
+
96
+ :param url: 请求URL
97
+ :return: HttpRequest对象
98
+ """
99
+ return HttpRequest.post(url)
100
+
101
+ @staticmethod
102
+ def download_string(url: str, charset: str = "utf-8") -> str:
103
+ """下载URL内容为字符串
104
+
105
+ :param url: 下载URL
106
+ :param charset: 字符集,默认utf-8
107
+ :return: 下载的字符串内容
108
+ """
109
+ response = HttpRequest.get(url).charset(charset).execute()
110
+ return response.to_str()
111
+
112
+ @staticmethod
113
+ def download_file(url: str, dest: str) -> int:
114
+ """下载URL内容到文件
115
+
116
+ :param url: 下载URL
117
+ :param dest: 目标文件路径
118
+ :return: 下载的字节数
119
+ """
120
+ import httpx
121
+
122
+ with httpx.stream("GET", url, follow_redirects=True, timeout=60) as response:
123
+ response.raise_for_status()
124
+ total = 0
125
+ with open(dest, "wb") as f:
126
+ for chunk in response.iter_bytes():
127
+ f.write(chunk)
128
+ total += len(chunk)
129
+ return total
130
+
131
+ @staticmethod
132
+ def download_bytes(url: str) -> bytes:
133
+ """下载URL内容为字节数组
134
+
135
+ :param url: 下载URL
136
+ :return: 下载的字节数组
137
+ """
138
+ response = HttpRequest.get(url).execute()
139
+ return response.to_bytes()
140
+
141
+ @staticmethod
142
+ def to_params(param_map: dict, charset: str = "utf-8") -> str:
143
+ """将参数Map转换为URL查询字符串
144
+
145
+ :param param_map: 参数字典
146
+ :param charset: 字符集,默认utf-8
147
+ :return: URL编码后的查询字符串,如 "key1=value1&key2=value2"
148
+ """
149
+ if not param_map:
150
+ return ""
151
+ encoded_pairs = []
152
+ for key, value in param_map.items():
153
+ encoded_key = quote(str(key), encoding=charset)
154
+ encoded_value = quote(str(value), encoding=charset) if value is not None else ""
155
+ encoded_pairs.append(f"{encoded_key}={encoded_value}")
156
+ return "&".join(encoded_pairs)
157
+
158
+ @staticmethod
159
+ def decode_param_map(params_str: str) -> Dict[str, str]:
160
+ """将URL查询字符串解码为单值参数字典
161
+
162
+ 如果某个键对应多个值,只取第一个值。
163
+
164
+ :param params_str: URL查询字符串,如 "key1=value1&key2=value2"
165
+ :return: 参数字典,值为单个字符串
166
+ """
167
+ if not params_str:
168
+ return {}
169
+ result: Dict[str, str] = {}
170
+ parsed = parse_qs(params_str, keep_blank_values=True)
171
+ for key, values in parsed.items():
172
+ result[key] = values[0] if values else ""
173
+ return result
174
+
175
+ @staticmethod
176
+ def decode_params(params_str: str) -> Dict[str, List[str]]:
177
+ """将URL查询字符串解码为多值参数字典
178
+
179
+ :param params_str: URL查询字符串,如 "key=value1&key=value2"
180
+ :return: 参数字典,值为字符串列表
181
+ """
182
+ if not params_str:
183
+ return {}
184
+ return parse_qs(params_str, keep_blank_values=True)
185
+
186
+ @staticmethod
187
+ def url_with_form(url: str, form: dict) -> str:
188
+ """将表单参数附加到URL上
189
+
190
+ :param url: 基础URL
191
+ :param form: 表单参数字典
192
+ :return: 附加参数后的完整URL
193
+ """
194
+ if not form:
195
+ return url
196
+ params_str = HttpUtil.to_params(form)
197
+ if "?" in url:
198
+ if url.endswith("&") or url.endswith("?"):
199
+ return url + params_str
200
+ return url + "&" + params_str
201
+ return url + "?" + params_str
202
+
203
+ @staticmethod
204
+ def get_charset(content_type: str) -> str:
205
+ """从Content-Type中提取字符集
206
+
207
+ :param content_type: Content-Type头值,如 "text/html; charset=utf-8"
208
+ :return: 字符集名称,如 "utf-8";如果未指定则返回 "utf-8"
209
+ """
210
+ if not content_type:
211
+ return "utf-8"
212
+ match = re.search(r"charset\s*=\s*([^\s;]+)", content_type, re.IGNORECASE)
213
+ if match:
214
+ return match.group(1).strip().strip('"').strip("'")
215
+ return "utf-8"
216
+
217
+ @staticmethod
218
+ def encode_url(url_str: str) -> str:
219
+ """URL编码
220
+
221
+ :param url_str: 待编码的URL字符串
222
+ :return: 编码后的URL字符串
223
+ """
224
+ if not url_str:
225
+ return ""
226
+ return quote(url_str, safe=":/?#[]@!$&'()*+,;=-._~%")
227
+
228
+ @staticmethod
229
+ def decode_url(url_str: str) -> str:
230
+ """URL解码
231
+
232
+ :param url_str: 待解码的URL字符串
233
+ :return: 解码后的URL字符串
234
+ """
235
+ if not url_str:
236
+ return ""
237
+ return unquote(url_str)
hutool/json_util.py ADDED
@@ -0,0 +1,251 @@
1
+ import json
2
+ import re
3
+ from typing import Any, List, Optional, Type, Union
4
+
5
+
6
+ class JSONUtil:
7
+ """JSON工具类"""
8
+
9
+ @staticmethod
10
+ def create_obj() -> dict:
11
+ """创建空JSON对象"""
12
+ return {}
13
+
14
+ @staticmethod
15
+ def create_array() -> list:
16
+ """创建空JSON数组"""
17
+ return []
18
+
19
+ # ------------------------------------------------------------------ #
20
+ # 解析
21
+ # ------------------------------------------------------------------ #
22
+
23
+ @staticmethod
24
+ def parse_obj(json_str: str) -> dict:
25
+ """解析JSON字符串为字典"""
26
+ result = json.loads(json_str)
27
+ if not isinstance(result, dict):
28
+ raise ValueError("JSON字符串不是对象类型")
29
+ return result
30
+
31
+ @staticmethod
32
+ def parse_array(json_str: str) -> list:
33
+ """解析JSON字符串为列表"""
34
+ result = json.loads(json_str)
35
+ if not isinstance(result, list):
36
+ raise ValueError("JSON字符串不是数组类型")
37
+ return result
38
+
39
+ @staticmethod
40
+ def parse(json_str: str):
41
+ """解析JSON字符串,自动返回对应Python类型"""
42
+ return json.loads(json_str)
43
+
44
+ # ------------------------------------------------------------------ #
45
+ # 序列化
46
+ # ------------------------------------------------------------------ #
47
+
48
+ @staticmethod
49
+ def to_json_str(obj: Any, indent: Optional[int] = None) -> str:
50
+ """对象转JSON字符串
51
+
52
+ :param obj: 待序列化的对象
53
+ :param indent: 缩进空格数,None表示不缩进
54
+ """
55
+ return json.dumps(obj, ensure_ascii=False, indent=indent)
56
+
57
+ @staticmethod
58
+ def to_json_pretty_str(obj: Any) -> str:
59
+ """对象转格式化JSON字符串(缩进2空格)"""
60
+ return json.dumps(obj, ensure_ascii=False, indent=2)
61
+
62
+ # ------------------------------------------------------------------ #
63
+ # Bean 转换(简易实现,基于类构造器)
64
+ # ------------------------------------------------------------------ #
65
+
66
+ @staticmethod
67
+ def to_bean(json_str: str, bean_class: Type):
68
+ """JSON字符串转对象
69
+
70
+ 将JSON解析为字典后,通过 bean_class(\\*\\*dict) 构造实例。
71
+ 如果 bean_class 提供了 from_dict 类方法,则优先调用。
72
+ """
73
+ data = json.loads(json_str)
74
+ if hasattr(bean_class, "from_dict") and callable(bean_class.from_dict):
75
+ return bean_class.from_dict(data)
76
+ if isinstance(data, dict):
77
+ return bean_class(**data)
78
+ return bean_class(data)
79
+
80
+ @staticmethod
81
+ def to_bean_list(json_str: str, element_class: Type) -> list:
82
+ """JSON字符串转对象列表"""
83
+ data = json.loads(json_str)
84
+ if not isinstance(data, list):
85
+ raise ValueError("JSON字符串不是数组类型")
86
+ if hasattr(element_class, "from_dict") and callable(element_class.from_dict):
87
+ return [element_class.from_dict(item) for item in data]
88
+ return [element_class(**item) if isinstance(item, dict) else element_class(item) for item in data]
89
+
90
+ @staticmethod
91
+ def from_bean(obj: Any) -> str:
92
+ """对象转JSON字符串
93
+
94
+ 如果对象具有 to_dict 方法则先调用,否则直接序列化。
95
+ """
96
+ if hasattr(obj, "to_dict") and callable(obj.to_dict):
97
+ return json.dumps(obj.to_dict(), ensure_ascii=False)
98
+ return json.dumps(obj, ensure_ascii=False, default=str)
99
+
100
+ # ------------------------------------------------------------------ #
101
+ # 文件读写
102
+ # ------------------------------------------------------------------ #
103
+
104
+ @staticmethod
105
+ def read_json(path: str, charset: str = "utf-8"):
106
+ """读取JSON文件并解析"""
107
+ with open(path, encoding=charset) as f:
108
+ return json.load(f)
109
+
110
+ @staticmethod
111
+ def read_json_object(path: str, charset: str = "utf-8") -> dict:
112
+ """读取JSON文件为字典"""
113
+ result = JSONUtil.read_json(path, charset)
114
+ if not isinstance(result, dict):
115
+ raise ValueError("JSON文件内容不是对象类型")
116
+ return result
117
+
118
+ @staticmethod
119
+ def read_json_array(path: str, charset: str = "utf-8") -> list:
120
+ """读取JSON文件为列表"""
121
+ result = JSONUtil.read_json(path, charset)
122
+ if not isinstance(result, list):
123
+ raise ValueError("JSON文件内容不是数组类型")
124
+ return result
125
+
126
+ @staticmethod
127
+ def write_json(path: str, obj: Any, charset: str = "utf-8", indent: Optional[int] = None) -> None:
128
+ """写入JSON文件
129
+
130
+ :param path: 文件路径
131
+ :param obj: 待写入对象
132
+ :param charset: 文件编码
133
+ :param indent: 缩进空格数,None表示不缩进
134
+ """
135
+ with open(path, "w", encoding=charset) as f:
136
+ json.dump(obj, f, ensure_ascii=False, indent=indent)
137
+
138
+ # ------------------------------------------------------------------ #
139
+ # 校验
140
+ # ------------------------------------------------------------------ #
141
+
142
+ @staticmethod
143
+ def is_json(str_val: str) -> bool:
144
+ """是否为有效JSON"""
145
+ try:
146
+ json.loads(str_val)
147
+ return True
148
+ except (json.JSONDecodeError, TypeError):
149
+ return False
150
+
151
+ @staticmethod
152
+ def is_json_obj(str_val: str) -> bool:
153
+ """是否为JSON对象"""
154
+ try:
155
+ return isinstance(json.loads(str_val), dict)
156
+ except (json.JSONDecodeError, TypeError):
157
+ return False
158
+
159
+ @staticmethod
160
+ def is_json_array(str_val: str) -> bool:
161
+ """是否为JSON数组"""
162
+ try:
163
+ return isinstance(json.loads(str_val), list)
164
+ except (json.JSONDecodeError, TypeError):
165
+ return False
166
+
167
+ # ------------------------------------------------------------------ #
168
+ # 格式化 / 压缩
169
+ # ------------------------------------------------------------------ #
170
+
171
+ @staticmethod
172
+ def format_json(json_str: str, indent: int = 2) -> str:
173
+ """格式化JSON字符串"""
174
+ obj = json.loads(json_str)
175
+ return json.dumps(obj, ensure_ascii=False, indent=indent)
176
+
177
+ @staticmethod
178
+ def compress(json_str: str) -> str:
179
+ """压缩JSON(去除所有空白)"""
180
+ obj = json.loads(json_str)
181
+ return json.dumps(obj, ensure_ascii=False, separators=(",", ":"))
182
+
183
+ # ------------------------------------------------------------------ #
184
+ # 路径操作(支持 'a.b.c' 和 'a[0].b')
185
+ # ------------------------------------------------------------------ #
186
+
187
+ @staticmethod
188
+ def _parse_path_keys(path: str) -> List[Union[str, int]]:
189
+ """将路径字符串解析为键列表
190
+
191
+ 例如 'a.b[0].c' -> ['a', 'b', 0, 'c']
192
+ """
193
+ keys: List[Union[str, int]] = []
194
+ for part in re.split(r"\.(?![^\[]*\])", path):
195
+ # 处理 key[index] 形式
196
+ match = re.match(r"^(\w+)((?:\[\d+\])*)$", part)
197
+ if match:
198
+ keys.append(match.group(1))
199
+ for idx in re.findall(r"\[(\d+)\]", match.group(2)):
200
+ keys.append(int(idx))
201
+ elif part:
202
+ keys.append(part)
203
+ return keys
204
+
205
+ @staticmethod
206
+ def get_by_path(json_data: Any, path: str) -> Any:
207
+ """按路径获取值
208
+
209
+ :param json_data: JSON数据(字典或列表)
210
+ :param path: 路径,如 'a.b.c' 或 'a[0].b'
211
+ :return: 对应路径的值,路径不存在时返回 None
212
+ """
213
+ keys = JSONUtil._parse_path_keys(path)
214
+ current = json_data
215
+ for key in keys:
216
+ if current is None:
217
+ return None
218
+ try:
219
+ current = current[key]
220
+ except (KeyError, IndexError, TypeError):
221
+ return None
222
+ return current
223
+
224
+ @staticmethod
225
+ def put_by_path(json_data: Any, path: str, value: Any) -> None:
226
+ """按路径设置值
227
+
228
+ :param json_data: JSON数据(字典或列表)
229
+ :param path: 路径,如 'a.b.c' 或 'a[0].b'
230
+ :param value: 要设置的值
231
+ """
232
+ keys = JSONUtil._parse_path_keys(path)
233
+ if not keys:
234
+ raise ValueError("路径不能为空")
235
+ current = json_data
236
+ for key in keys[:-1]:
237
+ next_key = keys[keys.index(key) + 1] if keys.index(key) + 1 < len(keys) else None
238
+ if isinstance(current, dict):
239
+ if key not in current:
240
+ current[key] = {} if isinstance(next_key, str) else []
241
+ current = current[key]
242
+ elif isinstance(current, list):
243
+ while len(current) <= key:
244
+ current.append(None)
245
+ if current[key] is None:
246
+ current[key] = {} if isinstance(next_key, str) else []
247
+ current = current[key]
248
+ else:
249
+ raise TypeError(f"无法在路径 '{path}' 上设置值:中间节点类型不支持")
250
+ last_key = keys[-1]
251
+ current[last_key] = value
hutool/jwt_util.py ADDED
@@ -0,0 +1,57 @@
1
+ """JWT工具类"""
2
+
3
+
4
+ import jwt
5
+
6
+
7
+ class JWTUtil:
8
+ """JWT工具类"""
9
+
10
+ @staticmethod
11
+ def create_token(payload: dict, secret: str, algorithm: str = "HS256") -> str:
12
+ """创建JWT token
13
+
14
+ :param payload: 载荷数据
15
+ :param secret: 密钥
16
+ :param algorithm: 加密算法,默认为 'HS256'
17
+ :return: JWT token字符串
18
+ """
19
+ return jwt.encode(payload, secret, algorithm=algorithm)
20
+
21
+ @staticmethod
22
+ def parse_token(token: str, secret: str, algorithm: str = "HS256") -> dict:
23
+ """解析并验证JWT token
24
+
25
+ :param token: JWT token字符串
26
+ :param secret: 密钥
27
+ :param algorithm: 加密算法,默认为 'HS256'
28
+ :return: 解析后的载荷数据
29
+ :raises jwt.ExpiredSignatureError: token已过期
30
+ :raises jwt.InvalidTokenError: token无效
31
+ """
32
+ return jwt.decode(token, secret, algorithms=[algorithm])
33
+
34
+ @staticmethod
35
+ def verify(token: str, secret: str, algorithm: str = "HS256") -> bool:
36
+ """验证token是否有效
37
+
38
+ :param token: JWT token字符串
39
+ :param secret: 密钥
40
+ :param algorithm: 加密算法,默认为 'HS256'
41
+ :return: token是否有效
42
+ """
43
+ try:
44
+ jwt.decode(token, secret, algorithms=[algorithm])
45
+ return True
46
+ except jwt.PyJWTError:
47
+ return False
48
+
49
+ @staticmethod
50
+ def get_payload(token: str) -> dict:
51
+ """获取token的payload(不验证签名)
52
+
53
+ :param token: JWT token字符串
54
+ :return: 解析后的载荷数据
55
+ :raises jwt.DecodeError: token格式无效
56
+ """
57
+ return jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256", "HS384", "HS512"])
@@ -0,0 +1,5 @@
1
+ from .props_util import PropsUtil
2
+ from .setting_util import SettingUtil
3
+ from .yaml_util import YamlUtil
4
+
5
+ __all__ = ["PropsUtil", "SettingUtil", "YamlUtil"]
@@ -0,0 +1,79 @@
1
+ """Properties文件工具类"""
2
+
3
+ import os
4
+ import re
5
+ from typing import Any
6
+
7
+
8
+ class PropsUtil:
9
+ """Properties文件工具类
10
+
11
+ 加载和解析标准 .properties 格式的配置文件。
12
+ """
13
+
14
+ @staticmethod
15
+ def load(path: str, charset: str = "utf-8") -> dict:
16
+ """加载.properties文件
17
+
18
+ 支持的格式:
19
+ - key=value
20
+ - key: value
21
+ - key value
22
+ - # 注释行
23
+ - ! 注释行
24
+ - 续行符(行尾 \\)
25
+
26
+ :param path: .properties文件路径
27
+ :param charset: 文件编码,默认为 'utf-8'
28
+ :return: 解析后的属性字典
29
+ """
30
+ path = os.path.abspath(path)
31
+ props: dict = {}
32
+ with open(path, encoding=charset) as f:
33
+ lines = f.readlines()
34
+
35
+ continued_line = ""
36
+ for line in lines:
37
+ line = line.strip()
38
+ # 跳过空行和注释
39
+ if not line or line.startswith("#") or line.startswith("!"):
40
+ continue
41
+
42
+ # 处理续行
43
+ if continued_line:
44
+ line = continued_line + line
45
+ continued_line = ""
46
+
47
+ if line.endswith("\\"):
48
+ continued_line = line[:-1]
49
+ continue
50
+
51
+ # 解析 key=value 或 key: value 或 key value
52
+ match = re.match(r"^([^=: \t]+)[ \t]*[=: \t](.*)$", line)
53
+ if match:
54
+ key = match.group(1).strip()
55
+ value = match.group(2).strip()
56
+ props[key] = PropsUtil._unescape(value)
57
+
58
+ return props
59
+
60
+ @staticmethod
61
+ def get(props: dict, key: str, default: Any = None) -> Any:
62
+ """获取属性值
63
+
64
+ :param props: 属性字典
65
+ :param key: 属性键
66
+ :param default: 默认值
67
+ :return: 属性值,不存在时返回默认值
68
+ """
69
+ return props.get(key, default)
70
+
71
+ @staticmethod
72
+ def _unescape(value: str) -> str:
73
+ """反转义properties值中的特殊字符
74
+
75
+ :param value: 原始值
76
+ :return: 反转义后的值
77
+ """
78
+ result = value.replace("\\n", "\n").replace("\\t", "\t").replace("\\\\", "\\")
79
+ return result