simtoolsz 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/PKG-INFO +1 -1
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/pyproject.toml +1 -1
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/__init__.py +1 -1
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/mail.py +29 -13
- simtoolsz-0.2.1/tests/verify_unicode_fix.py +93 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/.github/workflows/publish.yml +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/.gitignore +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/.python-version +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/LICENSE +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/README.md +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/README_EN.md +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/docs/DATETIME_CONVERSION.md +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/docs/mail_usage_guide.md +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/examples/conversion_examples.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/examples/mail_examples.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/examples/today_examples.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/examples/zip2db_example.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/requirements-dev.lock +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/requirements.lock +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/datetime.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/io.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/utils.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/src/simtoolsz/youtube.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_conversion.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_iso_comprehensive.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_iso_format.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_simple.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_today_optimized.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_which_format.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_zip2db.py +0 -0
- {simtoolsz-0.2.0 → simtoolsz-0.2.1}/tests/test_zip2db_simple.py +0 -0
|
@@ -9,7 +9,7 @@ import simtoolsz.datetime as datetime
|
|
|
9
9
|
try:
|
|
10
10
|
__version__ = importlib.metadata.version("simtoolsz")
|
|
11
11
|
except importlib.metadata.PackageNotFoundError:
|
|
12
|
-
__version__ = "0.2.
|
|
12
|
+
__version__ = "0.2.1"
|
|
13
13
|
|
|
14
14
|
__all__ = [
|
|
15
15
|
'__version__', 'mail', 'utils', 'datetime', 'io'
|
|
@@ -137,8 +137,11 @@ def send_email(
|
|
|
137
137
|
|
|
138
138
|
# 设置发件人
|
|
139
139
|
if sender_name:
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
# 使用Header处理非ASCII字符,然后手动格式化地址
|
|
141
|
+
encoded_name = Header(sender_name, "utf-8").encode()
|
|
142
|
+
msg["From"] = f"{encoded_name} <{email_account}>"
|
|
143
|
+
else:
|
|
144
|
+
msg["From"] = email_account
|
|
142
145
|
|
|
143
146
|
def format_recipient_list(recipient_list):
|
|
144
147
|
"""格式化收件人列表"""
|
|
@@ -146,8 +149,12 @@ def send_email(
|
|
|
146
149
|
for recipient in recipient_list:
|
|
147
150
|
if isinstance(recipient, tuple) and len(recipient) == 2:
|
|
148
151
|
name, addr = recipient
|
|
149
|
-
|
|
150
|
-
|
|
152
|
+
if name:
|
|
153
|
+
# 使用Header处理非ASCII字符,然后手动格式化地址
|
|
154
|
+
encoded_name = Header(name, "utf-8").encode()
|
|
155
|
+
formatted.append(f"{encoded_name} <{addr}>")
|
|
156
|
+
else:
|
|
157
|
+
formatted.append(addr)
|
|
151
158
|
else:
|
|
152
159
|
# 处理字符串格式如 "张三<user@example.com>"
|
|
153
160
|
recipient_str = str(recipient)
|
|
@@ -155,8 +162,9 @@ def send_email(
|
|
|
155
162
|
name_part = recipient_str.split("<")[0].strip()
|
|
156
163
|
addr_part = recipient_str.split("<")[1].split(">")[0].strip()
|
|
157
164
|
if name_part:
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
# 使用Header处理非ASCII字符,然后手动格式化地址
|
|
166
|
+
encoded_name = Header(name_part, "utf-8").encode()
|
|
167
|
+
formatted.append(f"{encoded_name} <{addr_part}>")
|
|
160
168
|
else:
|
|
161
169
|
formatted.append(addr_part)
|
|
162
170
|
else:
|
|
@@ -165,9 +173,15 @@ def send_email(
|
|
|
165
173
|
|
|
166
174
|
# 设置收件人
|
|
167
175
|
msg["Subject"] = Header(subject, "utf-8")
|
|
168
|
-
|
|
176
|
+
|
|
177
|
+
# 处理收件人,确保正确编码
|
|
178
|
+
if all_recipients:
|
|
179
|
+
formatted_recipients = format_recipient_list(all_recipients)
|
|
180
|
+
msg["To"] = ", ".join(formatted_recipients)
|
|
181
|
+
|
|
169
182
|
if all_cc:
|
|
170
|
-
|
|
183
|
+
formatted_cc = format_recipient_list(all_cc)
|
|
184
|
+
msg["Cc"] = ", ".join(formatted_cc)
|
|
171
185
|
|
|
172
186
|
# 处理邮件正文和签名
|
|
173
187
|
full_content = content
|
|
@@ -202,8 +216,9 @@ def send_email(
|
|
|
202
216
|
_, subtype = mime_type.split("/", 1)
|
|
203
217
|
img_part = MIMEImage(img_data, _subtype=subtype)
|
|
204
218
|
img_part.add_header("Content-ID", f"<{cid}>")
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
# 正确处理内嵌图片文件名编码
|
|
220
|
+
encoded_filename = Header(img_path.name, "utf-8").encode()
|
|
221
|
+
img_part.add_header("Content-Disposition", "inline", filename=encoded_filename)
|
|
207
222
|
msg.attach(img_part)
|
|
208
223
|
except Exception as e:
|
|
209
224
|
raise RuntimeError(f"处理内嵌图片失败 {img_path}: {e}")
|
|
@@ -218,9 +233,10 @@ def send_email(
|
|
|
218
233
|
with open(file_path, "rb") as f:
|
|
219
234
|
file_data = f.read()
|
|
220
235
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
236
|
+
# 正确处理附件文件名编码
|
|
237
|
+
encoded_filename = Header(file_path.name, "utf-8").encode()
|
|
238
|
+
part = MIMEApplication(file_data, Name=encoded_filename)
|
|
239
|
+
part.add_header("Content-Disposition", "attachment", filename=encoded_filename)
|
|
224
240
|
msg.attach(part)
|
|
225
241
|
except Exception as e:
|
|
226
242
|
raise RuntimeError(f"处理附件失败 {file_path}: {e}")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
邮件Unicode编码修复验证脚本
|
|
5
|
+
|
|
6
|
+
这个脚本验证修复的邮件发送功能是否能正确处理包含中文和其他
|
|
7
|
+
非ASCII字符的情况,避免'ascii' codec编码错误。
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
|
13
|
+
|
|
14
|
+
from simtoolsz.mail import send_email
|
|
15
|
+
|
|
16
|
+
def test_unicode_email_sending():
|
|
17
|
+
"""测试Unicode字符邮件发送修复"""
|
|
18
|
+
print("=== 邮件Unicode编码修复验证 ===\n")
|
|
19
|
+
|
|
20
|
+
test_cases = [
|
|
21
|
+
{
|
|
22
|
+
"name": "中文发件人和收件人",
|
|
23
|
+
"sender_name": "张三",
|
|
24
|
+
"recipients": ["李四<recipient@example.com>"],
|
|
25
|
+
"subject": "中文主题测试",
|
|
26
|
+
"content": "这是一封包含中文内容的测试邮件。"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "混合字符测试",
|
|
30
|
+
"sender_name": "Admin管理员",
|
|
31
|
+
"recipients": ["Test用户<recipient@example.com>"],
|
|
32
|
+
"subject": "Mixed Test 混合测试",
|
|
33
|
+
"content": "This email contains both English and 中文内容 for testing."
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
all_passed = True
|
|
38
|
+
|
|
39
|
+
for i, test_case in enumerate(test_cases, 1):
|
|
40
|
+
print(f"测试 {i}: {test_case['name']}")
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
result = send_email(
|
|
44
|
+
email_account="test@example.com",
|
|
45
|
+
password="test123",
|
|
46
|
+
subject=test_case['subject'],
|
|
47
|
+
content=test_case['content'],
|
|
48
|
+
recipients=test_case['recipients'],
|
|
49
|
+
sender_name=test_case['sender_name'],
|
|
50
|
+
smtp_config={
|
|
51
|
+
"smtp_server": "smtp.example.com",
|
|
52
|
+
"port": 587,
|
|
53
|
+
"use_ssl": False
|
|
54
|
+
},
|
|
55
|
+
timeout=3 # 短超时,因为我们只测试编码不实际发送
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# 检查是否出现ASCII编码错误
|
|
59
|
+
error_msg = result['message'].lower()
|
|
60
|
+
if "ascii" in error_msg and "codec" in error_msg:
|
|
61
|
+
print(f"❌ ASCII编码错误仍然存在: {result['message']}")
|
|
62
|
+
all_passed = False
|
|
63
|
+
else:
|
|
64
|
+
print(f"✅ Unicode编码处理正常")
|
|
65
|
+
print(f" 消息: {result['message']}")
|
|
66
|
+
|
|
67
|
+
except UnicodeEncodeError as e:
|
|
68
|
+
print(f"❌ Unicode编码错误: {e}")
|
|
69
|
+
all_passed = False
|
|
70
|
+
except Exception as e:
|
|
71
|
+
# 其他错误(如网络连接失败)是可以接受的,因为我们没有真实的服务器
|
|
72
|
+
error_msg = str(e).lower()
|
|
73
|
+
if "ascii" in error_msg and "codec" in error_msg:
|
|
74
|
+
print(f"❌ ASCII编码错误: {e}")
|
|
75
|
+
all_passed = False
|
|
76
|
+
else:
|
|
77
|
+
print(f"✅ 无编码错误(其他错误可接受): {e}")
|
|
78
|
+
|
|
79
|
+
print()
|
|
80
|
+
|
|
81
|
+
return all_passed
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
success = test_unicode_email_sending()
|
|
85
|
+
|
|
86
|
+
if success:
|
|
87
|
+
print("🎉 邮件Unicode编码修复验证通过!")
|
|
88
|
+
print("修复成功解决了 'ascii' codec can't encode characters 错误。")
|
|
89
|
+
else:
|
|
90
|
+
print("❌ 邮件Unicode编码修复验证失败。")
|
|
91
|
+
print("仍然存在编码问题,需要进一步修复。")
|
|
92
|
+
|
|
93
|
+
sys.exit(0 if success else 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|