【Python】Flask-Mail发送邮件报错解决方案(UnicodeEncodeError 'ascii' codec can't encode characters in position 52-55 ordinal not in range(128))
✨报错提示
完全相同的代码在macOS上运行正常
在Windows上运行报错
报错提示如下:
Traceback (most recent call last):
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask\app.py", line 2528, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask\app.py", line 1825, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask\app.py", line 1823, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask\app.py", line 1799, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "E:\Code\Study\Flask_Study\Flask05_Q&A_Platform\blueprints\auth.py", line 94, in get_email_captcha
mail.send(message)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask_mail.py", line 492, in send
message.send(connection)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask_mail.py", line 427, in send
connection.send(self)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask_mail.py", line 190, in send
message.as_bytes() if PY3 else message.as_string(),
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\site-packages\flask_mail.py", line 385, in as_bytes
return self._message().as_bytes()
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\email\message.py", line 178, in as_bytes
g.flatten(self, unixfrom=unixfrom)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\email\generator.py", line 116, in flatten
self._write(msg)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\email\generator.py", line 199, in _write
self._write_headers(msg)
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\email\generator.py", line 422, in _write_headers
self._fp.write(self.policy.fold_binary(h, v))
File "C:\Users\Doubl\miniconda3\envs\Temp\lib\email\policy.py", line 202, in fold_binary
return folded.encode(charset, 'surrogateescape')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 53-56: ordinal not in range(128)
✨错误排查
根据报错提示 我们定位到环境外部库的
${conda_dir}\miniconda3\envs\${env_name}\lib\email\policy.py
阅读EmailPolicy
源码可知
@_extend_docstrings
class EmailPolicy(Policy):
"""+
PROVISIONAL
The API extensions enabled by this policy are currently provisional.
Refer to the documentation for details.
This policy adds new header parsing and folding algorithms. Instead of
simple strings, headers are custom objects with custom attributes
depending on the type of the field. The folding algorithm fully
implements RFCs 2047 and 5322.
In addition to the settable attributes listed above that apply to
all Policies, this policy adds the following additional attributes:
utf8 -- if False (the default) message headers will be
serialized as ASCII, using encoded words to encode
any non-ASCII characters in the source strings. If
True, the message headers will be serialized using
utf8 and will not contain encoded words (see RFC
6532 for more on this serialization format).
refold_source -- if the value for a header in the Message object
came from the parsing of some source, this attribute
indicates whether or not a generator should refold
that value when transforming the message back into
stream form. The possible values are:
none -- all source values use original folding
long -- source values that have any line that is
longer than max_line_length will be
refolded
all -- all values are refolded.
The default is 'long'.
header_factory -- a callable that takes two arguments, 'name' and
'value', where 'name' is a header field name and
'value' is an unfolded header field value, and
returns a string-like object that represents that
header. A default header_factory is provided that
understands some of the RFC5322 header field types.
(Currently address fields and date fields have
special treatment, while all other fields are
treated as unstructured. This list will be
completed before the extension is marked stable.)
content_manager -- an object with at least two methods: get_content
and set_content. When the get_content or
set_content method of a Message object is called,
it calls the corresponding method of this object,
passing it the message object as its first argument,
and any arguments or keywords that were passed to
it as additional arguments. The default
content_manager is
:data:`~email.contentmanager.raw_data_manager`.
"""
同时我们阅读导致报错的fold_binary
方法
def fold_binary(self, name, value):
"""+
The same as fold if cte_type is 7bit, except that the returned value is
bytes.
If cte_type is 8bit, non-ASCII binary data is converted back into
bytes. Headers with binary data are not refolded, regardless of the
refold_header setting, since there is no way to know whether the binary
data consists of single byte characters or multibyte characters.
If utf8 is true, headers are encoded to utf8, otherwise to ascii with
non-ASCII unicode rendered as encoded words.
"""
folded = self._fold(name, value, refold_binary=self.cte_type=='7bit')
charset = 'utf8' if self.utf8 else 'ascii'
return folded.encode(charset, 'surrogateescape')
尝试在charset = 'utf8' if self.utf8 else 'ascii'
后添加print(charset)
重新启动项目
!一定要重新启动项目 Flask的debug=True
对于外部库文件变化不会自动重新启动项目
输出结果
127.0.0.1 - - [27/Mar/2023 15:10:37] "GET /auth/mail/test?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 304 -
charset: ascii
charset: ascii
charset: ascii
charset: ascii
charset: ascii
charset: ascii
charset: ascii
charset: ascii
127.0.0.1 - - [27/Mar/2023 15:12:07] "GET /auth/mail/test HTTP/1.1" 500 -
因此我们重新回到policy.py
删除添加的print(charset)
可以确定问题是charset = 'utf8' if self.utf8 else 'ascii'
这行代码导致
✨解决方案
反复阅读外部库源码
没有找到可以传入charset
参数的地方
本解决方案涉及到修改外部库源代码
每次更换环境都需要修改
建议使用其他module开发邮件发送模块
建议使用其他module开发邮件发送模块
建议使用其他module开发邮件发送模块
定位到${conda_dir}\miniconda3\envs\${env_name}\lib\email\policy.py
第88行
刚好在构造函数前面
message_factory = EmailMessage
utf8 = False
refold_source = 'long'
header_factory = HeaderRegistry()
content_manager = raw_data_manager
def __init__(self, **kw):
# Ensure that each new instance gets a unique header factory
# (as opposed to clones, which share the factory).
if 'header_factory' not in kw:
object.__setattr__(self, 'header_factory', HeaderRegistry())
super().__init__(**kw)
修改utf8 = False
为utf8 = True
⭐转载请注明出处
本文作者:双份浓缩馥芮白
原文链接:https://www.cnblogs.com/Flat-White/p/17261697.html
版权所有,如需转载请注明出处。