Ограничения PyTuple_SetItem

У меня есть модуль расширения Python, который создает кортеж как атрибут другого объекта и устанавливает элементы в кортеж. Всякий раз, когда я выполняю этот модуль в Python, я получаю сообщение об ошибке SystemError: неверный аргумент внутренней функции

. Прочитав документацию для PyTuple и отладив свою программу в течение нескольких часов, я все еще не мог понять, что, черт возьми, происходит. Запуск моей программы через отладчик показал, что проблема возникла при вызове библиотеки внутри интерпретатора Python. Итак, наконец, я взглянул на исходный код Python и наконец осознал проблему. Функция PyTuple_SetItem имеет интересное ограничение, о котором я не знал и не могу найти явно задокументированного.

Вот важная функция в исходном коде Python (отредактирована для ясности):

int PyTuple_SetItem(register PyObject *op, register Py_ssize_t i, PyObject *newitem)
{
    .....
    if (!PyTuple_Check(op) || op->ob_refcnt != 1) {
        Py_XDECREF(newitem);
        PyErr_BadInternalCall();
        return -1;
    }
    .....
}

Важной строкой здесь является условие op-> ob_refcnt! = 1 . Итак, проблема: вы даже не можете вызвать PyTuple_SetItem , если у Tuple нет счетчика ссылок 1. Похоже, идея здесь в том, что вы никогда не должны использовать PyTuple_SetItem за исключением случаев, когда вы создаете кортеж с помощью PyTuple_New () . Я думаю, это имеет смысл, поскольку в конце концов кортежи должны быть неизменяемыми, поэтому это ограничение помогает сохранить ваш код C в большей степени в соответствии с абстракциями системы типов Python.

Однако я нигде не могу найти это ограничение задокументировано. Соответствующие документы, похоже, находятся здесь и здесь , ни одна из которых не определяет это ограничение. Документы в основном говорят, что когда вы вызываете PyTuple_New (X) , все элементы в кортеже инициализируются значением NULL . Поскольку NULL не является допустимым значением Python, программист модуля расширения должен убедиться, что все слоты в кортеже заполнены правильными значениями Python, прежде чем возвращать кортеж интерпретатору. Но нигде не говорится, что это нужно делать, пока у объекта Tuple счетчик ссылок равен 1.

Итак, проблема в том, что я, по сути, загнал себя в угол, потому что не знал об этом (недокументированном?) Ограничении на PyTuple_SetItem . Мой код структурирован таким образом, что очень неудобно вставлять элементы в кортеж до тех пор, пока после сам кортеж не станет атрибутом другого объекта. Итак, когда приходит время заполнить элементы в кортеже, кортеж уже имеет более высокое количество ссылок.

Мне, вероятно, придется реструктурировать свой код, но я серьезно рассматривал возможность просто временно установить счетчик ссылок для Кортеж до 1, вставка элементов и восстановление исходного счетчика ссылок. Конечно, я знаю, что это ужасный взлом, и это не какое-то постоянное решение. Несмотря на это, я Я хочу знать, документировано ли где-либо требование относительно количества ссылок в Tuple. Это просто деталь реализации CPython или это то, на что пользователи API могут рассчитывать в качестве ожидаемого поведения?

11
задан denfromufa 3 February 2018 в 12:04
поделиться