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,231 @@
1
+ """正则表达式工具类,对应 Java cn.hutool.core.util.ReUtil"""
2
+
3
+ import re
4
+ from typing import List, Optional
5
+
6
+
7
+ class ReUtil:
8
+ """正则表达式工具类,对应 Java cn.hutool.core.util.ReUtil
9
+
10
+ 提供常用的正则匹配、提取、替换、分割等静态方法。
11
+ """
12
+
13
+ @staticmethod
14
+ def is_match(pattern: str, content: str) -> bool:
15
+ """给定内容是否匹配正则(全匹配)
16
+
17
+ :param pattern: 正则表达式
18
+ :param content: 被匹配的内容
19
+ :return: 是否全匹配
20
+ """
21
+ return re.fullmatch(pattern, content) is not None
22
+
23
+ @staticmethod
24
+ def contains(pattern: str, content: str) -> bool:
25
+ """给定内容是否包含正则匹配
26
+
27
+ :param pattern: 正则表达式
28
+ :param content: 被匹配的内容
29
+ :return: 是否包含匹配
30
+ """
31
+ return re.search(pattern, content) is not None
32
+
33
+ @staticmethod
34
+ def get(pattern: str, content: str, group_index: int = 0) -> Optional[str]:
35
+ """获得匹配的字符串,group_index=0 返回整个匹配
36
+
37
+ :param pattern: 正则表达式
38
+ :param content: 被匹配的内容
39
+ :param group_index: 匹配组索引,0表示整个匹配
40
+ :return: 匹配的字符串,无匹配返回 None
41
+ """
42
+ match = re.search(pattern, content)
43
+ if match is None:
44
+ return None
45
+ try:
46
+ return match.group(group_index)
47
+ except IndexError:
48
+ return None
49
+
50
+ @staticmethod
51
+ def get_all(pattern: str, content: str, group_index: int = 0) -> List[str]:
52
+ """获得所有匹配的字符串列表
53
+
54
+ :param pattern: 正则表达式
55
+ :param content: 被匹配的内容
56
+ :param group_index: 匹配组索引,0表示整个匹配
57
+ :return: 所有匹配的字符串列表
58
+ """
59
+ results: List[str] = []
60
+ for match in re.finditer(pattern, content):
61
+ try:
62
+ results.append(match.group(group_index))
63
+ except IndexError:
64
+ pass
65
+ return results
66
+
67
+ @staticmethod
68
+ def get_all_groups(pattern: str, content: str) -> List[List[str]]:
69
+ """获得所有匹配,每个匹配返回所有分组
70
+
71
+ :param pattern: 正则表达式
72
+ :param content: 被匹配的内容
73
+ :return: 二维列表,外层为每个匹配,内层为该匹配的所有捕获组(group(1) 至 group(N))
74
+ """
75
+ results: List[List[str]] = []
76
+ for match in re.finditer(pattern, content):
77
+ results.append(list(match.groups(default="")))
78
+ return results
79
+
80
+ @staticmethod
81
+ def match_all(pattern: str, content: str) -> List[str]:
82
+ """获得匹配的全部内容(等价于 get_all group_index=0)
83
+
84
+ :param pattern: 正则表达式
85
+ :param content: 被匹配的内容
86
+ :return: 所有完整匹配的字符串列表
87
+ """
88
+ return ReUtil.get_all(pattern, content, group_index=0)
89
+
90
+ @staticmethod
91
+ def replace_all(content: str, pattern: str, replacement: str) -> str:
92
+ """替换所有匹配
93
+
94
+ :param content: 被替换的内容
95
+ :param pattern: 正则表达式
96
+ :param replacement: 替换字符串,可使用 \\1 \\2 等反向引用
97
+ :return: 替换后的字符串
98
+ """
99
+ return re.sub(pattern, replacement, content)
100
+
101
+ @staticmethod
102
+ def replace_first(content: str, pattern: str, replacement: str) -> str:
103
+ """替换第一个匹配
104
+
105
+ :param content: 被替换的内容
106
+ :param pattern: 正则表达式
107
+ :param replacement: 替换字符串,可使用 \\1 \\2 等反向引用
108
+ :return: 替换后的字符串
109
+ """
110
+ return re.sub(pattern, replacement, content, count=1)
111
+
112
+ @staticmethod
113
+ def extract(pattern: str, content: str, template: str) -> Optional[str]:
114
+ """提取匹配内容并按模板重组
115
+
116
+ 例如::
117
+
118
+ extract(r"(\\d+)-(\\d+)", "123-456", "$2-$1") -> "456-123"
119
+ extract(r"(\\w+)@(\\w+)", "user@host", "$1") -> "user"
120
+
121
+ :param pattern: 正则表达式,应包含分组
122
+ :param content: 被匹配的内容
123
+ :param template: 模板字符串,使用 $1 $2 等引用分组
124
+ :return: 按模板重组后的字符串,无匹配返回 None
125
+ """
126
+ match = re.search(pattern, content)
127
+ if match is None:
128
+ return None
129
+ return ReUtil._apply_template(match, template)
130
+
131
+ @staticmethod
132
+ def extract_multi(pattern: str, content: str, template: str) -> List[str]:
133
+ """提取所有匹配内容并按模板重组
134
+
135
+ :param pattern: 正则表达式,应包含分组
136
+ :param content: 被匹配的内容
137
+ :param template: 模板字符串,使用 $1 $2 等引用分组
138
+ :return: 所有按模板重组后的字符串列表
139
+ """
140
+ results: List[str] = []
141
+ for match in re.finditer(pattern, content):
142
+ results.append(ReUtil._apply_template(match, template))
143
+ return results
144
+
145
+ @staticmethod
146
+ def count(pattern: str, content: str) -> int:
147
+ """统计匹配次数
148
+
149
+ :param pattern: 正则表达式
150
+ :param content: 被匹配的内容
151
+ :return: 匹配次数
152
+ """
153
+ return len(re.findall(pattern, content))
154
+
155
+ @staticmethod
156
+ def split(content: str, pattern: str) -> List[str]:
157
+ """按正则分割字符串
158
+
159
+ :param content: 被分割的字符串
160
+ :param pattern: 分隔符正则表达式
161
+ :return: 分割后的字符串列表
162
+ """
163
+ return re.split(pattern, content)
164
+
165
+ @staticmethod
166
+ def del_all(pattern: str, content: str) -> str:
167
+ """删除所有匹配内容
168
+
169
+ :param pattern: 正则表达式
170
+ :param content: 被处理的字符串
171
+ :return: 删除匹配内容后的字符串
172
+ """
173
+ return re.sub(pattern, "", content)
174
+
175
+ @staticmethod
176
+ def escape(characters: str) -> str:
177
+ """转义正则特殊字符
178
+
179
+ :param characters: 需要转义的字符串
180
+ :return: 转义后的字符串
181
+ """
182
+ return re.escape(characters)
183
+
184
+ @staticmethod
185
+ def get_group0(pattern: str, content: str) -> Optional[str]:
186
+ """获取第一个匹配的第 0 组(整个匹配)
187
+
188
+ :param pattern: 正则表达式
189
+ :param content: 被匹配的内容
190
+ :return: 整个匹配的字符串,无匹配返回 None
191
+ """
192
+ return ReUtil.get(pattern, content, group_index=0)
193
+
194
+ @staticmethod
195
+ def get_group1(pattern: str, content: str) -> Optional[str]:
196
+ """获取第一个匹配的第 1 组
197
+
198
+ :param pattern: 正则表达式
199
+ :param content: 被匹配的内容
200
+ :return: 第 1 组匹配的字符串,无匹配返回 None
201
+ """
202
+ return ReUtil.get(pattern, content, group_index=1)
203
+
204
+ # ------------------------------------------------------------------
205
+ # 内部辅助方法
206
+ # ------------------------------------------------------------------
207
+
208
+ @staticmethod
209
+ def _apply_template(match: re.Match, template: str) -> str:
210
+ """将匹配对象按模板重组
211
+
212
+ 模板中 $n(n 为数字)会被替换为对应的捕获组内容。
213
+ $$ 会被替换为字面量 $。
214
+
215
+ :param match: 正则匹配对象
216
+ :param template: 模板字符串
217
+ :return: 重组后的字符串
218
+ """
219
+
220
+ def _replace_ref(m: re.Match) -> str:
221
+ token = m.group(0)
222
+ if token == "$$":
223
+ return "$"
224
+ # $0, $1, $2, ...
225
+ idx = int(token[1:])
226
+ try:
227
+ return match.group(idx) if match.group(idx) is not None else ""
228
+ except IndexError:
229
+ return ""
230
+
231
+ return re.sub(r"\$\$|\$\d+", _replace_ref, template)
@@ -0,0 +1,135 @@
1
+ import inspect
2
+ from typing import Any, Dict, List, Type
3
+
4
+
5
+ class ReflectUtil:
6
+ """反射工具类
7
+
8
+ 提供对Python对象的反射操作,包括获取/设置字段值、调用方法、
9
+ 获取字段和方法信息等。
10
+ """
11
+
12
+ @staticmethod
13
+ def get_field_value(obj: Any, field_name: str) -> Any:
14
+ """获取字段值
15
+
16
+ :param obj: 对象
17
+ :param field_name: 字段名
18
+ :return: 字段值
19
+ :raises AttributeError: 字段不存在
20
+ """
21
+ return getattr(obj, field_name)
22
+
23
+ @staticmethod
24
+ def set_field_value(obj: Any, field_name: str, value: Any) -> None:
25
+ """设置字段值
26
+
27
+ 如果字段不存在则动态添加。
28
+
29
+ :param obj: 对象
30
+ :param field_name: 字段名
31
+ :param value: 字段值
32
+ """
33
+ setattr(obj, field_name, value)
34
+
35
+ @staticmethod
36
+ def invoke(obj: Any, method_name: str, *args: Any, **kwargs: Any) -> Any:
37
+ """调用方法
38
+
39
+ :param obj: 对象
40
+ :param method_name: 方法名
41
+ :param args: 位置参数
42
+ :param kwargs: 关键字参数
43
+ :return: 方法返回值
44
+ :raises AttributeError: 方法不存在
45
+ :raises TypeError: 方法不可调用
46
+ """
47
+ method = getattr(obj, method_name)
48
+ if not callable(method):
49
+ raise TypeError(f"属性 '{method_name}' 不可调用")
50
+ return method(*args, **kwargs)
51
+
52
+ @staticmethod
53
+ def get_fields(obj: Any) -> Dict[str, Any]:
54
+ """获取所有字段
55
+
56
+ 返回对象的所有非方法属性及其值的字典。
57
+
58
+ :param obj: 对象
59
+ :return: 字段名到字段值的字典
60
+ """
61
+ result = {}
62
+ for name, value in inspect.getmembers(obj):
63
+ if not name.startswith("_") and not callable(value):
64
+ result[name] = value
65
+ return result
66
+
67
+ @staticmethod
68
+ def get_methods(obj: Any) -> List[str]:
69
+ """获取所有方法名
70
+
71
+ 返回对象中不以单下划线开头的方法名称列表。
72
+
73
+ :param obj: 对象或类
74
+ :return: 方法名列表
75
+ """
76
+ return [name for name, value in inspect.getmembers(obj) if not name.startswith("_") and callable(value)]
77
+
78
+ @staticmethod
79
+ def has_field(obj: Any, field_name: str) -> bool:
80
+ """是否有指定字段
81
+
82
+ :param obj: 对象
83
+ :param field_name: 字段名
84
+ :return: 是否存在该字段
85
+ """
86
+ if not hasattr(obj, field_name):
87
+ return False
88
+ return not callable(getattr(obj, field_name))
89
+
90
+ @staticmethod
91
+ def has_method(obj: Any, method_name: str) -> bool:
92
+ """是否有指定方法
93
+
94
+ :param obj: 对象或类
95
+ :param method_name: 方法名
96
+ :return: 是否存在该方法
97
+ """
98
+ attr = getattr(obj, method_name, None)
99
+ return attr is not None and callable(attr)
100
+
101
+ @staticmethod
102
+ def get_annotations(obj: Any) -> Dict[str, Any]:
103
+ """获取类型注解
104
+
105
+ 获取对象(类或函数)的类型注解信息。
106
+
107
+ :param obj: 类或函数
108
+ :return: 类型注解字典
109
+ """
110
+ if isinstance(obj, type):
111
+ return getattr(obj, "__annotations__", {})
112
+ return getattr(obj, "__annotations__", {})
113
+
114
+ @staticmethod
115
+ def new_instance(cls: Type, *args: Any, **kwargs: Any) -> Any:
116
+ """创建实例
117
+
118
+ :param cls: 类
119
+ :param args: 位置参数
120
+ :param kwargs: 关键字参数
121
+ :return: 创建的实例
122
+ """
123
+ return cls(*args, **kwargs)
124
+
125
+ @staticmethod
126
+ def get_super_class(cls: Type) -> Type:
127
+ """获取父类
128
+
129
+ :param cls: 类
130
+ :return: 父类,如果没有父类则返回object
131
+ """
132
+ bases = cls.__bases__
133
+ if bases:
134
+ return bases[0]
135
+ return object
@@ -0,0 +1,89 @@
1
+ """运行时工具类"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ from typing import List, Optional
9
+
10
+
11
+ class RuntimeUtil:
12
+ """运行时工具类"""
13
+
14
+ @staticmethod
15
+ def exec(command: str, timeout: Optional[int] = None) -> str:
16
+ """执行系统命令,返回输出"""
17
+ result = subprocess.run(
18
+ command,
19
+ shell=True,
20
+ capture_output=True,
21
+ text=True,
22
+ timeout=timeout,
23
+ )
24
+ return result.stdout
25
+
26
+ @staticmethod
27
+ def exec_lines(command: str, timeout: Optional[int] = None) -> List[str]:
28
+ """执行系统命令,返回输出行列表"""
29
+ output = RuntimeUtil.exec(command, timeout=timeout)
30
+ lines = output.splitlines()
31
+ # 去除末尾空行
32
+ while lines and not lines[-1].strip():
33
+ lines.pop()
34
+ return lines
35
+
36
+ @staticmethod
37
+ def get_pid() -> int:
38
+ """获取当前进程PID"""
39
+ return os.getpid()
40
+
41
+ @staticmethod
42
+ def get_memory_info() -> dict:
43
+ """获取内存信息(单位字节)
44
+ 返回 {'total': ..., 'available': ..., 'used': ...}
45
+ 使用 /proc/meminfo (Linux) 或 psutil 回退
46
+ """
47
+ # 尝试使用 psutil(跨平台)
48
+ try:
49
+ import psutil # type: ignore
50
+
51
+ mem = psutil.virtual_memory()
52
+ return {
53
+ "total": mem.total,
54
+ "available": mem.available,
55
+ "used": mem.used,
56
+ }
57
+ except ImportError:
58
+ pass
59
+
60
+ # Linux 回退:读取 /proc/meminfo
61
+ if sys.platform.startswith("linux"):
62
+ meminfo = {}
63
+ try:
64
+ with open("/proc/meminfo") as f:
65
+ for line in f:
66
+ parts = line.split(":")
67
+ if len(parts) == 2:
68
+ key = parts[0].strip()
69
+ val = parts[1].strip().split()[0]
70
+ meminfo[key] = int(val) * 1024 # kB -> bytes
71
+ total = meminfo.get("MemTotal", 0)
72
+ available = meminfo.get("MemAvailable", meminfo.get("MemFree", 0))
73
+ return {
74
+ "total": total,
75
+ "available": available,
76
+ "used": total - available,
77
+ }
78
+ except OSError:
79
+ pass
80
+
81
+ return {"total": 0, "available": 0, "used": 0}
82
+
83
+ @staticmethod
84
+ def get_available_processors() -> int:
85
+ """获取可用CPU核心数"""
86
+ try:
87
+ return os.cpu_count() or 1
88
+ except Exception:
89
+ return 1