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,62 @@
1
+ """系统属性工具类"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import platform
7
+ import tempfile
8
+ from typing import Optional
9
+
10
+
11
+ class SystemUtil:
12
+ """系统属性工具类,对应 Java SystemPropsUtil"""
13
+
14
+ @staticmethod
15
+ def get(name: str, default: Optional[str] = None) -> Optional[str]:
16
+ """获取系统环境变量"""
17
+ return os.environ.get(name, default)
18
+
19
+ @staticmethod
20
+ def get_all() -> dict:
21
+ """获取所有环境变量"""
22
+ return dict(os.environ)
23
+
24
+ @staticmethod
25
+ def get_os_name() -> str:
26
+ """获取操作系统名称"""
27
+ return platform.system()
28
+
29
+ @staticmethod
30
+ def get_os_arch() -> str:
31
+ """获取操作系统架构"""
32
+ return platform.machine()
33
+
34
+ @staticmethod
35
+ def get_user_dir() -> str:
36
+ """获取用户当前工作目录"""
37
+ return os.getcwd()
38
+
39
+ @staticmethod
40
+ def get_user_home() -> str:
41
+ """获取用户主目录"""
42
+ return os.path.expanduser("~")
43
+
44
+ @staticmethod
45
+ def get_tmp_dir() -> str:
46
+ """获取临时目录"""
47
+ return tempfile.gettempdir()
48
+
49
+ @staticmethod
50
+ def get_file_separator() -> str:
51
+ """获取文件分隔符"""
52
+ return os.sep
53
+
54
+ @staticmethod
55
+ def get_line_separator() -> str:
56
+ """获取行分隔符"""
57
+ return os.linesep
58
+
59
+ @staticmethod
60
+ def is_windows() -> bool:
61
+ """是否Windows系统"""
62
+ return platform.system().lower() == "windows"
@@ -0,0 +1,232 @@
1
+ import re
2
+ from typing import Dict, List
3
+ from urllib.parse import parse_qs, parse_qsl, quote, unquote, urlencode, urlparse
4
+
5
+
6
+ class URLUtil:
7
+ """URL工具类,对应 Java cn.hutool.core.util.URLUtil"""
8
+
9
+ @staticmethod
10
+ def normalize(url_str: str) -> str:
11
+ """标准化URL,补齐协议头
12
+
13
+ 如果URL缺少协议头(http:// 或 https://),自动补充 http://。
14
+
15
+ :param url_str: 原始URL字符串
16
+ :return: 标准化后的URL字符串
17
+ """
18
+ if not url_str:
19
+ return url_str
20
+ url_str = url_str.strip()
21
+ if not re.match(r"^[a-zA-Z]+://", url_str):
22
+ url_str = "http://" + url_str
23
+ return url_str
24
+
25
+ @staticmethod
26
+ def encode(url_str: str, charset: str = "utf-8") -> str:
27
+ """URL编码
28
+
29
+ 对整个URL字符串进行编码。
30
+
31
+ :param url_str: 待编码的URL字符串
32
+ :param charset: 字符编码,默认utf-8
33
+ :return: 编码后的URL字符串
34
+ """
35
+ if not url_str:
36
+ return url_str
37
+ return quote(url_str, encoding=charset, safe=":/?#[]@!$&'()*+,;=-._~%")
38
+
39
+ @staticmethod
40
+ def decode(url_str: str, charset: str = "utf-8") -> str:
41
+ """URL解码
42
+
43
+ 对URL编码的字符串进行解码。
44
+
45
+ :param url_str: 待解码的URL字符串
46
+ :param charset: 字符编码,默认utf-8
47
+ :return: 解码后的URL字符串
48
+ """
49
+ if not url_str:
50
+ return url_str
51
+ return unquote(url_str, encoding=charset)
52
+
53
+ @staticmethod
54
+ def encode_params(params_str: str, charset: str = "utf-8") -> str:
55
+ """编码URL参数部分
56
+
57
+ 对URL查询参数字符串中的值部分进行编码。
58
+
59
+ :param params_str: 参数字符串,如 "key1=value1&key2=value2"
60
+ :param charset: 字符编码,默认utf-8
61
+ :return: 编码后的参数字符串
62
+ """
63
+ if not params_str:
64
+ return params_str
65
+ pairs = parse_qsl(params_str, keep_blank_values=True)
66
+ encoded_pairs = []
67
+ for key, value in pairs:
68
+ encoded_key = quote(key, encoding=charset, safe="")
69
+ encoded_value = quote(value, encoding=charset, safe="")
70
+ encoded_pairs.append(f"{encoded_key}={encoded_value}")
71
+ return "&".join(encoded_pairs)
72
+
73
+ @staticmethod
74
+ def get_path(url_str: str) -> str:
75
+ """获取URL的路径部分
76
+
77
+ :param url_str: URL字符串
78
+ :return: URL的路径部分,解析失败返回空字符串
79
+ """
80
+ if not url_str:
81
+ return ""
82
+ try:
83
+ parsed = urlparse(url_str)
84
+ return parsed.path
85
+ except Exception:
86
+ return ""
87
+
88
+ @staticmethod
89
+ def get_host(url_str: str) -> str:
90
+ """获取URL的主机部分
91
+
92
+ :param url_str: URL字符串
93
+ :return: URL的主机名,解析失败返回空字符串
94
+ """
95
+ if not url_str:
96
+ return ""
97
+ try:
98
+ parsed = urlparse(url_str)
99
+ return parsed.hostname or ""
100
+ except Exception:
101
+ return ""
102
+
103
+ @staticmethod
104
+ def get_port(url_str: str) -> int:
105
+ """获取URL的端口号,无端口返回-1
106
+
107
+ :param url_str: URL字符串
108
+ :return: 端口号,无端口或解析失败返回-1
109
+ """
110
+ if not url_str:
111
+ return -1
112
+ try:
113
+ parsed = urlparse(url_str)
114
+ return parsed.port if parsed.port is not None else -1
115
+ except Exception:
116
+ return -1
117
+
118
+ @staticmethod
119
+ def get_query(url_str: str) -> str:
120
+ """获取URL的查询参数部分
121
+
122
+ :param url_str: URL字符串
123
+ :return: URL的查询参数字符串(不含?),解析失败返回空字符串
124
+ """
125
+ if not url_str:
126
+ return ""
127
+ try:
128
+ parsed = urlparse(url_str)
129
+ return parsed.query
130
+ except Exception:
131
+ return ""
132
+
133
+ @staticmethod
134
+ def build_url(base: str, params: Dict[str, str]) -> str:
135
+ """构建带参数的URL
136
+
137
+ 在基础URL上附加参数,如果原URL已有参数则追加。
138
+
139
+ :param base: 基础URL
140
+ :param params: 参数字典
141
+ :return: 带参数的完整URL
142
+ """
143
+ if not base:
144
+ return ""
145
+ if not params:
146
+ return base
147
+
148
+ param_str = urlencode(params, encoding="utf-8")
149
+ separator = "&" if "?" in base else "?"
150
+ return f"{base}{separator}{param_str}"
151
+
152
+ @staticmethod
153
+ def url_with_form(url: str, form: Dict[str, str]) -> str:
154
+ """在URL后附加表单参数
155
+
156
+ 与build_url类似,将表单参数附加到URL后。
157
+
158
+ :param url: 原始URL
159
+ :param form: 表单参数字典
160
+ :return: 附加参数后的URL
161
+ """
162
+ return URLUtil.build_url(url, form)
163
+
164
+ @staticmethod
165
+ def to_params(param_map: Dict[str, str], charset: str = "utf-8") -> str:
166
+ """参数Map转URL参数字符串
167
+
168
+ :param param_map: 参数字典
169
+ :param charset: 字符编码,默认utf-8
170
+ :return: URL参数字符串,如 "key1=value1&key2=value2"
171
+ """
172
+ if not param_map:
173
+ return ""
174
+ return urlencode(param_map, encoding=charset)
175
+
176
+ @staticmethod
177
+ def decode_param_map(params_str: str, charset: str = "utf-8") -> Dict[str, str]:
178
+ """URL参数字符串转Map(每个key只保留最后一个值)
179
+
180
+ :param params_str: 参数字符串,如 "key1=value1&key2=value2"
181
+ :param charset: 字符编码,默认utf-8
182
+ :return: 参数字典,重复key时保留最后一个值
183
+ """
184
+ if not params_str:
185
+ return {}
186
+ pairs = parse_qsl(params_str, keep_blank_values=True, encoding=charset)
187
+ result: Dict[str, str] = {}
188
+ for key, value in pairs:
189
+ result[key] = value
190
+ return result
191
+
192
+ @staticmethod
193
+ def decode_params(params_str: str, charset: str = "utf-8") -> Dict[str, List[str]]:
194
+ """URL参数字符串转Map(每个key对应值列表)
195
+
196
+ :param params_str: 参数字符串,如 "key=value1&key=value2"
197
+ :param charset: 字符编码,默认utf-8
198
+ :return: 参数字典,每个key对应一个值列表
199
+ """
200
+ if not params_str:
201
+ return {}
202
+ return parse_qs(params_str, keep_blank_values=True, encoding=charset)
203
+
204
+ @staticmethod
205
+ def is_https(url_str: str) -> bool:
206
+ """是否为HTTPS
207
+
208
+ :param url_str: URL字符串
209
+ :return: 是HTTPS返回True,否则返回False
210
+ """
211
+ if not url_str:
212
+ return False
213
+ try:
214
+ parsed = urlparse(url_str)
215
+ return parsed.scheme.lower() == "https"
216
+ except Exception:
217
+ return False
218
+
219
+ @staticmethod
220
+ def is_http(url_str: str) -> bool:
221
+ """是否为HTTP
222
+
223
+ :param url_str: URL字符串
224
+ :return: 是HTTP返回True,否则返回False
225
+ """
226
+ if not url_str:
227
+ return False
228
+ try:
229
+ parsed = urlparse(url_str)
230
+ return parsed.scheme.lower() == "http"
231
+ except Exception:
232
+ return False
@@ -0,0 +1,41 @@
1
+ """版本比较工具类"""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class VersionUtil:
7
+ """版本比较工具类"""
8
+
9
+ @staticmethod
10
+ def compare(version1: str, version2: str) -> int:
11
+ """比较两个版本号,1: v1>v2, 0: 相等, -1: v1<v2
12
+ 支持 "1.2.3" 格式
13
+ """
14
+ v1_parts = version1.split(".")
15
+ v2_parts = version2.split(".")
16
+ max_len = max(len(v1_parts), len(v2_parts))
17
+
18
+ for i in range(max_len):
19
+ v1 = int(v1_parts[i]) if i < len(v1_parts) else 0
20
+ v2 = int(v2_parts[i]) if i < len(v2_parts) else 0
21
+ if v1 > v2:
22
+ return 1
23
+ if v1 < v2:
24
+ return -1
25
+
26
+ return 0
27
+
28
+ @staticmethod
29
+ def is_greater(version1: str, version2: str) -> bool:
30
+ """version1是否大于version2"""
31
+ return VersionUtil.compare(version1, version2) == 1
32
+
33
+ @staticmethod
34
+ def is_lower(version1: str, version2: str) -> bool:
35
+ """version1是否小于version2"""
36
+ return VersionUtil.compare(version1, version2) == -1
37
+
38
+ @staticmethod
39
+ def get_main_version(version: str) -> str:
40
+ """获取主版本号,如 "1.2.3" -> "1" """
41
+ return version.split(".")[0]
@@ -0,0 +1,158 @@
1
+ import re
2
+ import xml.etree.ElementTree as ET
3
+ from typing import Any, Dict, Optional
4
+ from xml.dom import minidom
5
+
6
+
7
+ class XmlUtil:
8
+ """XML工具类"""
9
+
10
+ @staticmethod
11
+ def read_xml(xml_str_or_path: str) -> ET.Element:
12
+ """读取XML(字符串或文件路径)
13
+
14
+ 支持传入XML字符串或文件路径,自动判断并解析。
15
+
16
+ :param xml_str_or_path: XML字符串或文件路径
17
+ :return: 解析后的XML根元素
18
+ """
19
+ xml_str_or_path = xml_str_or_path.strip()
20
+ if xml_str_or_path.startswith("<"):
21
+ return ET.fromstring(xml_str_or_path)
22
+ else:
23
+ tree = ET.parse(xml_str_or_path)
24
+ return tree.getroot()
25
+
26
+ @staticmethod
27
+ def parse_to_map(xml_str: str) -> dict:
28
+ """XML转字典
29
+
30
+ 将XML字符串递归转换为嵌套字典结构。如果同名子元素出现多次,值为列表。
31
+
32
+ :param xml_str: XML字符串
33
+ :return: 转换后的字典
34
+ """
35
+ root = ET.fromstring(xml_str) if xml_str.strip().startswith("<") else ET.parse(xml_str).getroot()
36
+
37
+ def _element_to_dict(element: ET.Element) -> Any:
38
+ children = list(element)
39
+ if not children:
40
+ text = element.text
41
+ if text is not None:
42
+ text = text.strip()
43
+ return text if text else ""
44
+
45
+ result: Dict[str, Any] = {}
46
+ for child in children:
47
+ child_data = _element_to_dict(child)
48
+ if child.tag in result:
49
+ existing = result[child.tag]
50
+ if isinstance(existing, list):
51
+ existing.append(child_data)
52
+ else:
53
+ result[child.tag] = [existing, child_data]
54
+ else:
55
+ result[child.tag] = child_data
56
+
57
+ # 添加属性
58
+ if element.attrib:
59
+ for key, value in element.attrib.items():
60
+ result[f"@{key}"] = value
61
+
62
+ return result
63
+
64
+ tag = root.tag
65
+ return {tag: _element_to_dict(root)}
66
+
67
+ @staticmethod
68
+ def to_xml_str(root_name: str, data: dict) -> str:
69
+ """字典转XML字符串
70
+
71
+ :param root_name: 根元素名称
72
+ :param data: 待转换的字典数据
73
+ :return: XML字符串
74
+ """
75
+ return XmlUtil.map_to_xml(data, root_name)
76
+
77
+ @staticmethod
78
+ def map_to_xml(data: dict, root_name: str = "root") -> str:
79
+ """字典转XML
80
+
81
+ 将字典递归转换为XML字符串。
82
+
83
+ :param data: 待转换的字典数据
84
+ :param root_name: 根元素名称,默认为"root"
85
+ :return: XML字符串
86
+ """
87
+ root = ET.Element(root_name)
88
+ XmlUtil._build_xml(root, data)
89
+ return ET.tostring(root, encoding="unicode")
90
+
91
+ @staticmethod
92
+ def _build_xml(parent: ET.Element, data: Any) -> None:
93
+ """递归构建XML元素
94
+
95
+ :param parent: 父元素
96
+ :param data: 当前数据(字典或值)
97
+ """
98
+ if isinstance(data, dict):
99
+ for key, value in data.items():
100
+ if key.startswith("@"):
101
+ # XML属性
102
+ parent.set(key[1:], str(value))
103
+ elif isinstance(value, list):
104
+ for item in value:
105
+ child = ET.SubElement(parent, key)
106
+ XmlUtil._build_xml(child, item)
107
+ else:
108
+ child = ET.SubElement(parent, key)
109
+ XmlUtil._build_xml(child, value)
110
+ else:
111
+ parent.text = str(data) if data is not None else ""
112
+
113
+ @staticmethod
114
+ def format_xml(xml_str: str) -> str:
115
+ """格式化XML
116
+
117
+ 将XML字符串格式化为带缩进的可读格式。
118
+
119
+ :param xml_str: 待格式化的XML字符串
120
+ :return: 格式化后的XML字符串
121
+ """
122
+ dom = minidom.parseString(xml_str)
123
+ pretty_xml = dom.toprettyxml(indent=" ", encoding=None)
124
+ # 去除minidom添加的多余空行
125
+ lines = [line for line in pretty_xml.split("\n") if line.strip()]
126
+ return "\n".join(lines)
127
+
128
+ @staticmethod
129
+ def get_element_text(element: ET.Element, xpath: str) -> Optional[str]:
130
+ """获取元素文本
131
+
132
+ 通过xpath查找子元素并返回其文本内容。
133
+
134
+ :param element: XML元素
135
+ :param xpath: 子元素的标签路径,如 "child/subchild"
136
+ :return: 元素文本内容,未找到则返回None
137
+ """
138
+ target = element.find(xpath)
139
+ if target is not None:
140
+ return target.text
141
+ return None
142
+
143
+ @staticmethod
144
+ def clean_invalid(xml_str: str) -> str:
145
+ """清理XML中的非法字符
146
+
147
+ 移除XML规范中不允许的控制字符(除了制表符、换行符、回车符)。
148
+
149
+ :param xml_str: 原始XML字符串
150
+ :return: 清理后的XML字符串
151
+ """
152
+ # 移除非法字符:XML合法字符为 #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
153
+ valid_xml_chars = re.compile(
154
+ r"[^\x09\x0A\x0D\x20-\x7E\x80-\xFF"
155
+ r"Ā-퟿-�"
156
+ r"\U00010000-\U0010FFFF]"
157
+ )
158
+ return valid_xml_chars.sub("", xml_str)
@@ -0,0 +1,126 @@
1
+ import gzip
2
+ import os
3
+ import zipfile
4
+ import zlib
5
+ from typing import Optional
6
+
7
+
8
+ class ZipUtil:
9
+ """压缩工具类"""
10
+
11
+ @staticmethod
12
+ def zip(src_path: str, dest_path: Optional[str] = None, charset: str = "utf-8") -> str:
13
+ """压缩文件或目录为zip
14
+
15
+ :param src_path: 源文件或目录路径
16
+ :param dest_path: 目标zip文件路径,默认为源路径加.zip后缀
17
+ :param charset: 文件名编码字符集,默认utf-8
18
+ :return: 生成的zip文件路径
19
+ """
20
+ if not os.path.exists(src_path):
21
+ raise FileNotFoundError(f"源路径不存在: {src_path}")
22
+
23
+ if dest_path is None:
24
+ dest_path = src_path + ".zip"
25
+
26
+ dest_path = os.path.abspath(dest_path)
27
+ src_path = os.path.abspath(src_path)
28
+
29
+ with zipfile.ZipFile(dest_path, "w", zipfile.ZIP_DEFLATED) as zf:
30
+ if os.path.isfile(src_path):
31
+ zf.write(src_path, os.path.basename(src_path))
32
+ elif os.path.isdir(src_path):
33
+ base_dir = os.path.basename(src_path)
34
+ for root, dirs, files in os.walk(src_path):
35
+ for file in files:
36
+ file_path = os.path.join(root, file)
37
+ arcname = os.path.join(base_dir, os.path.relpath(file_path, src_path))
38
+ zf.write(file_path, arcname)
39
+ else:
40
+ raise ValueError(f"不支持的路径类型: {src_path}")
41
+
42
+ return dest_path
43
+
44
+ @staticmethod
45
+ def unzip(zip_file: str, dest_path: str, charset: str = "utf-8") -> str:
46
+ """解压zip文件
47
+
48
+ :param zip_file: zip文件路径
49
+ :param dest_path: 解压目标目录
50
+ :param charset: 文件名编码字符集,默认utf-8
51
+ :return: 解压目标目录路径
52
+ """
53
+ if not os.path.exists(zip_file):
54
+ raise FileNotFoundError(f"zip文件不存在: {zip_file}")
55
+
56
+ dest_path = os.path.abspath(dest_path)
57
+ os.makedirs(dest_path, exist_ok=True)
58
+
59
+ with zipfile.ZipFile(zip_file, "r") as zf:
60
+ for info in zf.infolist():
61
+ # 尝试用指定编码解码文件名
62
+ try:
63
+ filename = info.filename.encode("cp437").decode(charset)
64
+ except (UnicodeDecodeError, UnicodeEncodeError):
65
+ filename = info.filename
66
+ info.filename = filename
67
+ zf.extract(info, dest_path)
68
+
69
+ return dest_path
70
+
71
+ @staticmethod
72
+ def gzip(data: bytes) -> bytes:
73
+ """Gzip压缩
74
+
75
+ :param data: 待压缩的字节数据
76
+ :return: 压缩后的字节数据
77
+ """
78
+ return gzip.compress(data)
79
+
80
+ @staticmethod
81
+ def gzip_str(data: str, charset: str = "utf-8") -> bytes:
82
+ """字符串Gzip压缩
83
+
84
+ :param data: 待压缩的字符串
85
+ :param charset: 字符编码,默认utf-8
86
+ :return: 压缩后的字节数据
87
+ """
88
+ return gzip.compress(data.encode(charset))
89
+
90
+ @staticmethod
91
+ def ungzip(data: bytes) -> bytes:
92
+ """Gzip解压
93
+
94
+ :param data: 待解压的字节数据
95
+ :return: 解压后的字节数据
96
+ """
97
+ return gzip.decompress(data)
98
+
99
+ @staticmethod
100
+ def ungzip_str(data: bytes, charset: str = "utf-8") -> str:
101
+ """Gzip解压为字符串
102
+
103
+ :param data: 待解压的字节数据
104
+ :param charset: 字符编码,默认utf-8
105
+ :return: 解压后的字符串
106
+ """
107
+ return gzip.decompress(data).decode(charset)
108
+
109
+ @staticmethod
110
+ def zlib_compress(data: bytes, level: int = -1) -> bytes:
111
+ """Zlib压缩
112
+
113
+ :param data: 待压缩的字节数据
114
+ :param level: 压缩级别,-1为默认,0为不压缩,1-9为压缩级别(1最快,9最小)
115
+ :return: 压缩后的字节数据
116
+ """
117
+ return zlib.compress(data, level)
118
+
119
+ @staticmethod
120
+ def zlib_uncompress(data: bytes) -> bytes:
121
+ """Zlib解压
122
+
123
+ :param data: 待解压的字节数据
124
+ :return: 解压后的字节数据
125
+ """
126
+ return zlib.decompress(data)
@@ -0,0 +1,4 @@
1
+ from .cron_pattern import CronPattern
2
+ from .cron_util import CronUtil
3
+
4
+ __all__ = ["CronPattern", "CronUtil"]