В чем разница между исходными кодами Python3.5 и Python3.6?

 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 ответ

Решение

Вы используете отладочную версию 3.5. В Python 3.5 и любой предыдущей версии нумерация строк в одном блоке байт-кода (т. Е. Байт-код модуля или функции) должна была быть монотонной, то есть каждый код операции должен отображаться на строку в исходном коде, чей номер белья должен быть больше или равно номеру строки предыдущего кода операции. Это когда-либо проверялось в отладочных сборках; в релизных сборках Python assert не будет скомпилирован, но сгенерированная вкладка номера строки в любом случае была бы недействительной.

Это обсуждалось в выпуске 26107 на http://bugs.python.org/. Требование монотонности номеров строк считалось вредным для оптимизаций, многие из которых реорганизуют сгенерированный байт-код. Таким образом, проверка была удалена в 3.6 вместе с другими изменениями, которые делают дельту номера строки целым числом со знаком.

Вы можете закомментировать это утверждение довольно безопасно, так как сборки релиза в любом случае устранили бы его, но не ожидайте, что отладка будет работать правильно в сгенерированном коде, так как вкладка номера строки теперь недопустима.

В качестве альтернативы, если вы реорганизуете строки в AST или что-то подобное, вы можете установить все номера строк равными 0, а не только отсутствующие; или вы можете генерировать фальшивые номера строк, которые не нарушают правило монотонности.


Случайная проблема возникла с генерируемыми AST, так как ast.fix_missing_locations написал бы номер строки 0 для любых узлов, у которых не было нумерации строк. Если части AST содержат номера строк, потому что они произошли от ast.parse, вероятно, что результирующее дерево AST нарушит требование монотонности - что опять-таки приведет только к проблемам на не-релизных сборках Pythons < 3.6.


Другое изменение, которое здесь не относится к ошибке, - это переход от байт-кода к слову-коду, который также был введен в Python 3.6. Здесь каждый код операции будет 16-разрядным словом вместо 8-разрядного байта с возможными расширенными аргументами. Вот почему смещение умножается на sizeof(_Py_CODEUNIT);,

Другие вопросы по тегам