Python3.5和Python3.6的源代码之间的区别是什么?

时间:2017-08-29 09:29:10

标签: python c python-3.x python-internals

 testing on ../../test/test_patm.py
python: Python/compile.c:4420: int assemble_lnotab(struct assembler *, 
struct instr *): Assertion `d_lineno >= 0' failed.
Aborted

运行我的测试程序时,我收到了上面给出的错误。 最后我发现Python3.5和Python3.6的源代码差别很小。 只有一行:

Python3.5

static int
assemble_lnotab(struct assembler *a, struct instr *i)
{
  int d_bytecode, d_lineno;
  Py_ssize_t len;
  unsigned char *lnotab;

  d_bytecode = a->a_offset - a->a_lineno_off;
  d_lineno = i->i_lineno - a->a_lineno;

  assert(d_bytecode >= 0);
  assert(d_lineno >= 0);   // the only difference

  if(d_bytecode == 0 && d_lineno == 0)
      return 1;
  ...

Python 3.6

static int
assemble_lnotab(struct assembler *a, struct instr *i)
{
    int d_bytecode, d_lineno;
    Py_ssize_t len;
    unsigned char *lnotab;

    d_bytecode = (a->a_offset - a->a_lineno_off) * sizeof(_Py_CODEUNIT);
    d_lineno = i->i_lineno - a->a_lineno;

    assert(d_bytecode >= 0);

    if(d_bytecode == 0 && d_lineno == 0)
        return 1;

如果我刚删除assert(d_lineno >= 0);怎么办?

1 个答案:

答案 0 :(得分:7)

您正在使用3.5的 debug 版本。在Python 3.5和任何以前的版本中,单个字节码块中的行号(即模块的字节码或函数)必须是单调,即每个操作码必须映射到源代码中的一行,其中的亚麻布必须大于或等于 上一个操作码的行号。在调试版本中检查过这个问题;在Python的发布版本中,assert不会被编译,但生成的行号选项卡无论如何都是无效的。

Issue 26107 bugs.python.org对此进行了讨论。对行数单调性的要求被认为对优化有害,其中许多将重新组织生成的字节码。因此,在3.6 中删除了检查以及其他更改,这使得行号delta成为有符号整数。

您可以非常安全地注释断言,因为版本构建无论如何都会消除它,但不要期望调试在生成的代码中正常工作作为行号标签现在无效。

作为替代方案,如果您重新组织AST中的行或类似的行,您可以将所有行号设置为0 - 而不仅仅是丢失的行号;或者你可以生成不会破坏单调性规则的虚假行号。

生成的AST出现了巧合问题,因为ast.fix_missing_locations会将 0 的行号写入任何缺少行号的节点。如果AST的某些部分包含行号,因为它们来自ast.parse,那么生成的AST树很可能会破坏单调性要求 - 这也只会导致Pythons的非释放构建问题< 3.6。

与此处的错误无关的另一个更改是从字节码到 wordcode 的更改,这也是在Python 3.6中引入的。这里每个操作码都是一个16位字,而不是一个8位字节,可能有扩展的args。这就是偏移量乘以sizeof(_Py_CODEUNIT);的原因。