Python编译器如何使用声明的编码预处理源文件?

Max*_*nko 6 python encoding python-internals

假设我有一个cp1251编码的Python 3源文件,其中包含以下内容:

# ????? (some Russian comment)
print('Hehehey')
Run Code Online (Sandbox Code Playgroud)

如果我运行该文件,我会得到这个:

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

这是明确的和预期的 - 我理解,通常,cp1251字节序列不能用UTF-8解码,UTF-8是Python 3中的默认编码.

但是如果我按如下方式编辑文件:

# coding: utf-8
# ????? (some Russian comment)
print('Hehehey')  
Run Code Online (Sandbox Code Playgroud)

一切都会好起来的.

这非常令人困惑.
在第二个例子中,我在源代码中仍然有相同的cp1251字节序列,这在UTF-8中无效,我希望编译器应该使用相同的编码(UTF-8)来预处理文件并以相同的错误终止.
我已经阅读了PEP 263,但仍然没有得到它没有发生的原因.

那么,为什么我的代码在第二种情况下工作并在第一种情况下终止?


UPD.

为了检查我的文本编辑器是否足够聪明,因为该行更改了文件的编码# coding: utf-8,让我们看看实际的字节:

(第1个例子)

23 20 fd fe ff fa fc ...
Run Code Online (Sandbox Code Playgroud)

(第2个例子)

23 20 63 6f 64 69 6e 67 3a 20 75 74 66 2d 38 0a
23 20 fd fe ff fa fc ...
Run Code Online (Sandbox Code Playgroud)

这些f字节用于cp1251中的西里尔字母,它们在UTF-8中无效.

此外,如果我以这种方式编辑源代码:

# coding: utf-8
# ????? (some Russian comment)
print('Hehehey')
print('?????')
Run Code Online (Sandbox Code Playgroud)

我将面临错误:

SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0xfd ...

所以,不幸的是我的文本编辑器并不那么聪明.
因此,在上面的例子中,源文件不会从cp1251转换为UTF-8.

Sha*_*ger 7

这似乎是如何强制执行默认编码的严格行为的一个怪癖.在tokenizer函数中decoding_gets,如果它还没有找到显式的编码声明(tok->encoding仍然是NULL),它会对该行进行逐字符检查,查找无效的UTF-8字符并弹出SyntaxError您正在看到的引用PEP 263.

但是如果已经指定了编码,check_coding_spec则会定义tok->encoding,并且完全绕过默认编码严格测试 ; 它不会替换为声明编码的测试.

通常情况下,这会导致问题实际上是被解析的代码时,但它看起来像注释在一个精简的方式处理:只要注释符,#被认可,分词器只是抓住并丢弃字符,直到它看到一个换行符或者EOF,它根本不试图对它们做任何事情(这是有道理的;解析评论只是浪费时间,可以花在实际运行的东西上).

因此,您观察到的行为:编码声明通过字符检查禁用严格的文件范围字符,检查在没有显式声明编码时应用的有效UTF-8,并且注释是特殊的,以便忽略其内容,允许垃圾用于转义检测的注释中的字节.