首页 > 解决方案 > UTF-16/UTF-32 中的 Python 脚本文件:如何解决 BOM 和 PEP 263 的冲突?

问题描述

由于最近发现的 Unicode特洛伊木马源攻击(也在PEP 672中描述),我更深入地研究了 Python 脚本的 Unicode/字符编码行为。在 Python 2 中,必须使用PEP 263中定义的特定编码行启用 Unicode 编码,并且从 Python 3 开始,UTF-8 被设置为 Python 文件 ( PEP 3120 ) 的默认编码。

为了检测(所有)可能的木马源攻击,我想以 UTF-16 和 UTF-32 创建示例 python 脚本,作为自定义 linter 的测试文件。

现在PEP 263定义第一行(如果第一行是第二行,则为第二行#!/usr/bin/python)不应包含任何非 ASCII 字符。相反,它首先需要一条特殊线路# -*- coding: <encoding name> -*-

这与定义 UTF-16 和 UTF-32 应包含 UTF-16 和 UTF-32 的 BOM 标记的 Unicode 标准(参见常见问题解答SO 问题)相冲突。

现在,如果我创建一个带有 BOM 标记的 UTF-16(或 UTF-32)文件,Python 会抱怨:

SyntaxError: Non-UTF-8 code starting with '\xff' in file test_utf_16.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

另一方面,如果我省略了 BOM 标记, Gitlab 和 Gitlab 都vim不会PyCharm正确显示代码(如果我不手动更改编码):

显示 NUL 字符的 PyCharm 和 VIM 的屏幕截图

更荒谬的是,如果我使用 UTF-16 或 UTF-32 对文件进行编码,Python (3.10) 不会抱怨,但也不会执行任何打印命令(即使 print 仅包含 ASCII)。

长话短说:

如何以 UTF-16 或 UTF-32 创建有效且工作的 Python 3 脚本文件,可以使用 vim 或 PyCharm(开箱即用)进行编辑?

更新:我还发现tokenize.detect_encoding即使字符串在文件中,实现 PEP 263 也无法确定正确的编码。因为在 UTF-16/UTF-32 中,第一行不再匹配 ASCII 正则表达式。

因此,似乎不太可能生成以 UTF-16 或 UTF-32 编码的有效 python 脚本。或者是吗?

附录

用于创建测试文件的 Python 代码

# create_examples.py
from pathlib import Path

base_path = Path(__file__).parent.absolute()


def write_encoding(enc: str, strip_endian: bool = False):
    encoding = enc.lower()
    if encoding.endswith('be') or encoding.endswith('le'):
        # As defined in unicode, if the le or the be is given, a BOM is not written
        bom_used = False
        if strip_endian:
            enc = enc[:-3]
    else:
        bom_used = True
    suffix = enc
    if bom_used:
        suffix += "_bom"
    name = f"test_{encoding}_{suffix}.py"
    content = f"# vim: set fileencoding={enc} :\n"
    content += f"print(\"{name}\")\n"
    content += 's = "x‏&quot; * 100  #    "‏x" is assigned'
    content += "\n"
    path = base_path / name
    path.write_bytes(content.encode(encoding))

write_encoding('utf_8')
write_encoding('utf_16')
write_encoding('utf_16_be', False)
write_encoding('utf_16_be', True)
write_encoding('utf_16_le', False)
write_encoding('utf_16_le', True)
write_encoding('utf_32')
write_encoding('utf_32_be', False)
write_encoding('utf_32_be', True)
write_encoding('utf_32_le', False)
write_encoding('utf_32_le', True)

执行所有测试文件 for i in $(ls test_*); do python $i; done

标签: pythonvimunicodecharacter-encodingpycharm

解决方案


推荐阅读