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.
- hutool/__init__.py +174 -0
- hutool/cache/__init__.py +7 -0
- hutool/cache/cache_util.py +47 -0
- hutool/cache/fifo_cache.py +87 -0
- hutool/cache/lfu_cache.py +129 -0
- hutool/cache/lru_cache.py +93 -0
- hutool/cache/timed_cache.py +115 -0
- hutool/captcha/__init__.py +3 -0
- hutool/captcha/captcha_util.py +215 -0
- hutool/core/__init__.py +23 -0
- hutool/core/_base.py +61 -0
- hutool/core/bean.py +214 -0
- hutool/core/codec.py +111 -0
- hutool/core/coll.py +635 -0
- hutool/core/date.py +1024 -0
- hutool/core/exceptions.py +66 -0
- hutool/core/io/__init__.py +0 -0
- hutool/core/io/data_size_util.py +79 -0
- hutool/core/io/file_name_util.py +111 -0
- hutool/core/io/file_util.py +650 -0
- hutool/core/io/io_util.py +133 -0
- hutool/core/io/path_util.py +247 -0
- hutool/core/io/resource_util.py +137 -0
- hutool/core/map.py +933 -0
- hutool/core/math_util.py +105 -0
- hutool/core/net.py +288 -0
- hutool/core/text/__init__.py +0 -0
- hutool/core/text/csv_util.py +54 -0
- hutool/core/text/str_builder.py +224 -0
- hutool/core/text/unicode_util.py +58 -0
- hutool/core/tree.py +242 -0
- hutool/core/util/__init__.py +63 -0
- hutool/core/util/array_util.py +503 -0
- hutool/core/util/boolean_util.py +124 -0
- hutool/core/util/charset_util.py +60 -0
- hutool/core/util/class_util.py +136 -0
- hutool/core/util/coordinate_util.py +186 -0
- hutool/core/util/credit_code_util.py +110 -0
- hutool/core/util/desensitized_util.py +194 -0
- hutool/core/util/enum_util.py +94 -0
- hutool/core/util/escape_util.py +97 -0
- hutool/core/util/hash_util.py +243 -0
- hutool/core/util/hex_util.py +140 -0
- hutool/core/util/id_util.py +147 -0
- hutool/core/util/idcard_util.py +300 -0
- hutool/core/util/number_util.py +720 -0
- hutool/core/util/object_util.py +294 -0
- hutool/core/util/page_util.py +61 -0
- hutool/core/util/phone_util.py +140 -0
- hutool/core/util/random_util.py +112 -0
- hutool/core/util/re_util.py +231 -0
- hutool/core/util/reflect_util.py +135 -0
- hutool/core/util/runtime_util.py +89 -0
- hutool/core/util/str_util.py +2320 -0
- hutool/core/util/system_util.py +62 -0
- hutool/core/util/url_util.py +232 -0
- hutool/core/util/version_util.py +41 -0
- hutool/core/util/xml_util.py +158 -0
- hutool/core/util/zip_util.py +126 -0
- hutool/cron/__init__.py +4 -0
- hutool/cron/cron_pattern.py +123 -0
- hutool/cron/cron_util.py +115 -0
- hutool/crypto/__init__.py +5 -0
- hutool/crypto/digest_util.py +167 -0
- hutool/crypto/secure_util.py +311 -0
- hutool/crypto/sign_util.py +74 -0
- hutool/dfa/__init__.py +3 -0
- hutool/dfa/sensitive_util.py +114 -0
- hutool/extra/__init__.py +6 -0
- hutool/extra/emoji_util.py +90 -0
- hutool/extra/pinyin_util.py +44 -0
- hutool/extra/qr_code_util.py +58 -0
- hutool/extra/template_util.py +41 -0
- hutool/http/__init__.py +6 -0
- hutool/http/html_util.py +88 -0
- hutool/http/http_request.py +188 -0
- hutool/http/http_response.py +139 -0
- hutool/http/http_util.py +237 -0
- hutool/json_util.py +251 -0
- hutool/jwt_util.py +57 -0
- hutool/setting/__init__.py +5 -0
- hutool/setting/props_util.py +79 -0
- hutool/setting/setting_util.py +80 -0
- hutool/setting/yaml_util.py +45 -0
- hutool_python-1.0.0.dist-info/LICENSE +127 -0
- hutool_python-1.0.0.dist-info/METADATA +438 -0
- hutool_python-1.0.0.dist-info/RECORD +89 -0
- hutool_python-1.0.0.dist-info/WHEEL +5 -0
- 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
|