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,58 @@
1
+ """二维码工具类,基于qrcode"""
2
+
3
+ import io
4
+
5
+ import qrcode
6
+ from PIL import Image
7
+
8
+
9
+ class QrCodeUtil:
10
+ """二维码工具类,基于qrcode"""
11
+
12
+ @staticmethod
13
+ def generate(content: str, width: int = 300, height: int = 300) -> Image.Image:
14
+ """生成二维码,返回PIL Image对象
15
+
16
+ :param content: 二维码内容
17
+ :param width: 图片宽度(像素)
18
+ :param height: 图片高度(像素)
19
+ :return: PIL Image对象
20
+ """
21
+ qr = qrcode.QRCode(
22
+ version=None,
23
+ error_correction=qrcode.constants.ERROR_CORRECT_H,
24
+ box_size=10,
25
+ border=4,
26
+ )
27
+ qr.add_data(content)
28
+ qr.make(fit=True)
29
+ img = qr.make_image(fill_color="black", back_color="white")
30
+ img = img.resize((width, height), Image.LANCZOS)
31
+ return img
32
+
33
+ @staticmethod
34
+ def generate_as_bytes(content: str, width: int = 300, height: int = 300, fmt: str = "png") -> bytes:
35
+ """生成二维码并返回字节数组
36
+
37
+ :param content: 二维码内容
38
+ :param width: 图片宽度(像素)
39
+ :param height: 图片高度(像素)
40
+ :param fmt: 图片格式,默认为 'png'
41
+ :return: 图片字节数组
42
+ """
43
+ img = QrCodeUtil.generate(content, width, height)
44
+ buf = io.BytesIO()
45
+ img.save(buf, format=fmt.upper())
46
+ return buf.getvalue()
47
+
48
+ @staticmethod
49
+ def generate_to_file(content: str, path: str, width: int = 300, height: int = 300) -> None:
50
+ """生成二维码并保存到文件
51
+
52
+ :param content: 二维码内容
53
+ :param path: 输出文件路径
54
+ :param width: 图片宽度(像素)
55
+ :param height: 图片高度(像素)
56
+ """
57
+ img = QrCodeUtil.generate(content, width, height)
58
+ img.save(path)
@@ -0,0 +1,41 @@
1
+ """模板工具类,基于jinja2"""
2
+
3
+ import os
4
+ from typing import Optional
5
+
6
+ from jinja2 import BaseLoader, Environment, FileSystemLoader, StrictUndefined
7
+
8
+
9
+ class TemplateUtil:
10
+ """模板工具类,基于jinja2"""
11
+
12
+ @staticmethod
13
+ def render(template_str: str, context: Optional[dict] = None) -> str:
14
+ """渲染模板字符串
15
+
16
+ :param template_str: 模板字符串
17
+ :param context: 上下文变量字典
18
+ :return: 渲染后的字符串
19
+ """
20
+ if context is None:
21
+ context = {}
22
+ env = Environment(loader=BaseLoader(), undefined=StrictUndefined)
23
+ template = env.from_string(template_str)
24
+ return template.render(**context)
25
+
26
+ @staticmethod
27
+ def render_file(template_path: str, context: Optional[dict] = None) -> str:
28
+ """渲染模板文件
29
+
30
+ :param template_path: 模板文件路径
31
+ :param context: 上下文变量字典
32
+ :return: 渲染后的字符串
33
+ """
34
+ if context is None:
35
+ context = {}
36
+ template_path = os.path.abspath(template_path)
37
+ template_dir = os.path.dirname(template_path)
38
+ template_name = os.path.basename(template_path)
39
+ env = Environment(loader=FileSystemLoader(template_dir), undefined=StrictUndefined)
40
+ template = env.get_template(template_name)
41
+ return template.render(**context)
@@ -0,0 +1,6 @@
1
+ from .html_util import HtmlUtil
2
+ from .http_request import HttpRequest
3
+ from .http_response import HttpResponse
4
+ from .http_util import HttpUtil
5
+
6
+ __all__ = ["HtmlUtil", "HttpRequest", "HttpResponse", "HttpUtil"]
@@ -0,0 +1,88 @@
1
+ import html
2
+ import re
3
+
4
+
5
+ class HtmlUtil:
6
+ """HTML工具类,提供HTML相关的常用处理方法"""
7
+
8
+ # 需要被替换的HTML空白字符(制表符、换行符、回车符)
9
+ _EMPTY_REGEX = re.compile(r"[\t\n\r]")
10
+
11
+ @staticmethod
12
+ def escape(html_str: str) -> str:
13
+ """HTML转义
14
+
15
+ 将特殊字符转换为HTML实体,防止XSS攻击。
16
+ 转义规则: & -> &amp; < -> &lt; > -> &gt; " -> &quot; ' -> &#x27;
17
+
18
+ :param html_str: 待转义的HTML字符串
19
+ :return: 转义后的安全字符串
20
+ """
21
+ if html_str is None:
22
+ return ""
23
+ return html.escape(html_str, quote=True)
24
+
25
+ @staticmethod
26
+ def unescape(html_str: str) -> str:
27
+ """HTML反转义
28
+
29
+ 将HTML实体转换回原始字符。
30
+
31
+ :param html_str: 待反转义的HTML字符串
32
+ :return: 反转义后的原始字符串
33
+ """
34
+ if html_str is None:
35
+ return ""
36
+ return html.unescape(html_str)
37
+
38
+ @staticmethod
39
+ def remove_html_tag(html_str: str) -> str:
40
+ """移除HTML标签
41
+
42
+ 移除字符串中的所有HTML标签,只保留纯文本内容。
43
+
44
+ :param html_str: 包含HTML标签的字符串
45
+ :return: 移除标签后的纯文本
46
+ """
47
+ if html_str is None:
48
+ return ""
49
+ # 移除HTML注释
50
+ text = re.sub(r"<!--.*?-->", "", html_str, flags=re.DOTALL)
51
+ # 移除script和style标签及其内容
52
+ text = re.sub(r"<script[^>]*>.*?</script>", "", text, flags=re.DOTALL | re.IGNORECASE)
53
+ text = re.sub(r"<style[^>]*>.*?</style>", "", text, flags=re.DOTALL | re.IGNORECASE)
54
+ # 移除所有HTML标签
55
+ text = re.sub(r"<[^>]+>", "", text)
56
+ # 清理多余空白
57
+ text = HtmlUtil._EMPTY_REGEX.sub(" ", text)
58
+ text = re.sub(r"\s+", " ", text)
59
+ return text.strip()
60
+
61
+ @staticmethod
62
+ def clean_html_tag(html_str: str) -> str:
63
+ """清理HTML标签
64
+
65
+ 移除字符串中的所有HTML标签,只保留纯文本内容。
66
+ 本方法与 remove_html_tag 功能相同。
67
+
68
+ :param html_str: 包含HTML标签的字符串
69
+ :return: 清理标签后的纯文本
70
+ """
71
+ return HtmlUtil.remove_html_tag(html_str)
72
+
73
+ @staticmethod
74
+ def wrap_html(text: str) -> str:
75
+ """包装为HTML段落
76
+
77
+ 将文本中的换行符转换为HTML的 <br> 标签,并用 <p> 标签包装。
78
+
79
+ :param text: 纯文本字符串
80
+ :return: 包装后的HTML段落字符串
81
+ """
82
+ if text is None:
83
+ return ""
84
+ # 转义HTML特殊字符
85
+ escaped = html.escape(text, quote=False)
86
+ # 将换行符替换为 <br> 标签
87
+ escaped = escaped.replace("\n", "<br>")
88
+ return f"<p>{escaped}</p>"
@@ -0,0 +1,188 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ import httpx
4
+
5
+
6
+ class HttpRequest:
7
+ """HTTP请求对象,支持链式调用
8
+
9
+ 示例::
10
+
11
+ response = (HttpRequest.get("https://example.com")
12
+ .header("Accept", "application/json")
13
+ .timeout(5000)
14
+ .execute())
15
+ """
16
+
17
+ def __init__(self, url: str, method: str = "GET"):
18
+ """构造HTTP请求
19
+
20
+ :param url: 请求URL
21
+ :param method: HTTP方法,默认GET
22
+ """
23
+ self._url: str = url
24
+ self._method: str = method.upper()
25
+ self._headers: Dict[str, str] = {}
26
+ self._cookies: Dict[str, str] = {}
27
+ self._form: Dict[str, str] = {}
28
+ self._body: Optional[str] = None
29
+ self._json_data: Any = None
30
+ self._timeout: int = 30000 # 毫秒
31
+ self._charset: str = "utf-8"
32
+ self._follow_redirects: bool = True
33
+ self._params: Optional[Dict[str, str]] = None
34
+
35
+ @classmethod
36
+ def get(cls, url: str) -> "HttpRequest":
37
+ """创建GET请求
38
+
39
+ :param url: 请求URL
40
+ :return: HttpRequest实例
41
+ """
42
+ return cls(url, "GET")
43
+
44
+ @classmethod
45
+ def post(cls, url: str) -> "HttpRequest":
46
+ """创建POST请求
47
+
48
+ :param url: 请求URL
49
+ :return: HttpRequest实例
50
+ """
51
+ return cls(url, "POST")
52
+
53
+ def method(self, method: str) -> "HttpRequest":
54
+ """设置HTTP方法
55
+
56
+ :param method: HTTP方法名,如GET、POST、PUT、DELETE等
57
+ :return: 当前实例,支持链式调用
58
+ """
59
+ self._method = method.upper()
60
+ return self
61
+
62
+ def header(self, name: str, value: str) -> "HttpRequest":
63
+ """设置单个请求头
64
+
65
+ :param name: 请求头名称
66
+ :param value: 请求头值
67
+ :return: 当前实例,支持链式调用
68
+ """
69
+ self._headers[name] = value
70
+ return self
71
+
72
+ def headers(self, headers: dict) -> "HttpRequest":
73
+ """批量设置请求头
74
+
75
+ :param headers: 请求头字典
76
+ :return: 当前实例,支持链式调用
77
+ """
78
+ self._headers.update(headers)
79
+ return self
80
+
81
+ def cookie(self, cookie: str) -> "HttpRequest":
82
+ """设置Cookie
83
+
84
+ 支持格式: "name=value" 或 "name1=value1; name2=value2"
85
+
86
+ :param cookie: Cookie字符串
87
+ :return: 当前实例,支持链式调用
88
+ """
89
+ if not cookie:
90
+ return self
91
+ for item in cookie.split(";"):
92
+ item = item.strip()
93
+ if "=" in item:
94
+ key, value = item.split("=", 1)
95
+ self._cookies[key.strip()] = value.strip()
96
+ return self
97
+
98
+ def form(self, key: str, value: Optional[str] = None) -> "HttpRequest":
99
+ """添加表单参数
100
+
101
+ :param key: 参数名
102
+ :param value: 参数值
103
+ :return: 当前实例,支持链式调用
104
+ """
105
+ if value is not None:
106
+ self._form[key] = value
107
+ return self
108
+
109
+ def body(self, body: str) -> "HttpRequest":
110
+ """设置请求体
111
+
112
+ :param body: 请求体字符串
113
+ :return: 当前实例,支持链式调用
114
+ """
115
+ self._body = body
116
+ return self
117
+
118
+ def json(self, json_data: Any) -> "HttpRequest":
119
+ """设置JSON请求体
120
+
121
+ :param json_data: JSON可序列化的数据
122
+ :return: 当前实例,支持链式调用
123
+ """
124
+ self._json_data = json_data
125
+ return self
126
+
127
+ def timeout(self, timeout: int) -> "HttpRequest":
128
+ """设置超时时间(毫秒)
129
+
130
+ :param timeout: 超时时间,单位毫秒
131
+ :return: 当前实例,支持链式调用
132
+ """
133
+ self._timeout = timeout
134
+ return self
135
+
136
+ def charset(self, charset: str) -> "HttpRequest":
137
+ """设置字符集
138
+
139
+ :param charset: 字符集名称,如 utf-8、gbk 等
140
+ :return: 当前实例,支持链式调用
141
+ """
142
+ self._charset = charset
143
+ return self
144
+
145
+ def follow_redirects(self, is_follow: bool) -> "HttpRequest":
146
+ """设置是否跟随重定向
147
+
148
+ :param is_follow: 是否跟随重定向
149
+ :return: 当前实例,支持链式调用
150
+ """
151
+ self._follow_redirects = is_follow
152
+ return self
153
+
154
+ def execute(self) -> "HttpResponse": # noqa: F821
155
+ """执行HTTP请求
156
+
157
+ :return: HttpResponse响应对象
158
+ :raises httpx.HTTPStatusError: 当响应状态码表示错误时
159
+ :raises httpx.RequestError: 当请求发生网络错误时
160
+ """
161
+ # 导入放在此处避免循环导入
162
+ from .http_response import HttpResponse
163
+
164
+ timeout_seconds = self._timeout / 1000.0
165
+
166
+ kwargs: Dict[str, Any] = {
167
+ "method": self._method,
168
+ "url": self._url,
169
+ "headers": self._headers,
170
+ "cookies": self._cookies,
171
+ "timeout": timeout_seconds,
172
+ "follow_redirects": self._follow_redirects,
173
+ }
174
+
175
+ if self._params:
176
+ kwargs["params"] = self._params
177
+
178
+ if self._json_data is not None:
179
+ kwargs["json"] = self._json_data
180
+ elif self._body is not None:
181
+ kwargs["content"] = self._body.encode(self._charset)
182
+ elif self._form:
183
+ kwargs["data"] = self._form
184
+
185
+ with httpx.Client() as client:
186
+ response = client.request(**kwargs)
187
+
188
+ return HttpResponse(response, charset=self._charset)
@@ -0,0 +1,139 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ import httpx
4
+
5
+
6
+ class HttpResponse:
7
+ """HTTP响应对象
8
+
9
+ 封装httpx.Response,提供便捷的响应数据访问方法。
10
+ """
11
+
12
+ def __init__(self, response: httpx.Response, charset: str = "utf-8"):
13
+ """构造HTTP响应
14
+
15
+ :param response: httpx响应对象
16
+ :param charset: 字符集,默认utf-8
17
+ """
18
+ self._response: httpx.Response = response
19
+ self._charset: str = charset
20
+
21
+ @property
22
+ def status(self) -> int:
23
+ """获取HTTP状态码
24
+
25
+ :return: HTTP状态码,如200、404等
26
+ """
27
+ return self._response.status_code
28
+
29
+ @property
30
+ def headers(self) -> Dict[str, str]:
31
+ """获取响应头
32
+
33
+ :return: 响应头字典
34
+ """
35
+ return dict(self._response.headers)
36
+
37
+ @property
38
+ def body(self) -> str:
39
+ """获取响应体字符串
40
+
41
+ :return: 响应体字符串
42
+ """
43
+ return self._response.text
44
+
45
+ @property
46
+ def content_length(self) -> int:
47
+ """获取响应体长度
48
+
49
+ :return: 响应体字节长度
50
+ """
51
+ content_length = self._response.headers.get("content-length")
52
+ if content_length is not None:
53
+ return int(content_length)
54
+ return len(self._response.content)
55
+
56
+ @property
57
+ def charset(self) -> str:
58
+ """获取字符集
59
+
60
+ :return: 字符集名称
61
+ """
62
+ return self._charset
63
+
64
+ @property
65
+ def content_type(self) -> str:
66
+ """获取Content-Type
67
+
68
+ :return: Content-Type头值
69
+ """
70
+ return self._response.headers.get("content-type", "")
71
+
72
+ @property
73
+ def url(self) -> str:
74
+ """获取最终响应的URL(考虑重定向)
75
+
76
+ :return: 响应的URL字符串
77
+ """
78
+ return str(self._response.url)
79
+
80
+ def is_ok(self) -> bool:
81
+ """判断状态码是否为2xx(成功)
82
+
83
+ :return: 状态码在200-299范围内返回True
84
+ """
85
+ return 200 <= self.status < 300
86
+
87
+ def to_bytes(self) -> bytes:
88
+ """获取响应体的字节数组
89
+
90
+ :return: 响应体字节数组
91
+ """
92
+ return self._response.content
93
+
94
+ def to_json(self) -> Any:
95
+ """将响应体解析为JSON
96
+
97
+ :return: 解析后的JSON对象(dict或list)
98
+ :raises json.JSONDecodeError: 响应体不是有效JSON时抛出
99
+ """
100
+ return self._response.json()
101
+
102
+ def to_str(self) -> str:
103
+ """获取响应体字符串
104
+
105
+ 使用指定的字符集解码响应体。
106
+
107
+ :return: 响应体字符串
108
+ """
109
+ return self._response.text
110
+
111
+ def header(self, name: str) -> Optional[str]:
112
+ """获取指定名称的响应头
113
+
114
+ :param name: 响应头名称
115
+ :return: 响应头值,不存在时返回None
116
+ """
117
+ return self._response.headers.get(name)
118
+
119
+ def cookie(self, name: str) -> Optional[str]:
120
+ """获取指定名称的Cookie值
121
+
122
+ :param name: Cookie名称
123
+ :return: Cookie值,不存在时返回None
124
+ """
125
+ return self._response.cookies.get(name)
126
+
127
+ def __str__(self) -> str:
128
+ """返回响应的字符串表示
129
+
130
+ :return: 包含状态码和URL的字符串
131
+ """
132
+ return f"HttpResponse [status={self.status}, url={self.url}]"
133
+
134
+ def __repr__(self) -> str:
135
+ """返回响应的详细表示
136
+
137
+ :return: 包含状态码和URL的字符串
138
+ """
139
+ return self.__str__()