Эквивалент опции python -R, которая влияет на хэш ints
У нас есть большая коллекция кода python, которая принимает некоторый ввод и производит некоторый вывод.
Мы хотели бы гарантировать, что, учитывая идентичный ввод, мы производим идентичный вывод независимо от версии python или локальной среды. (например, выполняется ли код в Windows, Mac или Linux в 32-разрядной или 64-разрядной версии)
Мы применяем это в автоматическом тестовом наборе, запустив нашу программу как с опцией -R
, так и без нее, и сравним вывод, предполагая, что вытряхнете любые пятна, где наш выход случайно зависнет от итерации a dict
. (Самый распространенный источник недетерминизма в нашем коде)
Однако, поскольку мы недавно скорректировали наш код, чтобы также поддерживать python 3, мы обнаружили место, где наш вывод зависел частично от итерации над dict
, которая использовала int
в качестве ключей. Этот порядок итераций изменился в python3 по сравнению с python2 и делал наш вывод другим. Наши существующие тесты (все на python 2.7) этого не заметили. (Потому что -R
не влияет на хэш int
s). После обнаружения его легко исправить, но мы хотели бы его найти раньше.
Есть ли способ дальнейшего стресс-тестирования нашего кода и дать нам уверенность в том, что мы выяснили все места, где мы закончили неявно в зависимости от того, что может быть различным в версиях/средах python? Я думаю, что что-то вроде -R
или PYTHONHASHSEED
, которое применяется к числам, а также к объектам str
, bytes
и datetime
, может работать, но я открыт для других подходов. Тем не менее, мне хотелось бы, чтобы наша автоматическая тестовая машина нуждалась только в одной версии python, если это возможно.
Другой приемлемой альтернативой будет какой-то способ запустить наш код с помощью pypy tweaked, чтобы использовать другой порядок при итерации элементов из dict
; Я думаю, что наш код работает на pypy, хотя это не то, что мы когда-либо явно поддерживали. Однако, если какой-то эксперт pypy дает нам способ настроить порядок итераций словаря на разных прогонах, мы с этим будем работать.
Ответы
Ответ 1
Использование PyPy не лучший выбор здесь, учитывая, что он всегда сохраняет порядок вставки в своих dicts (с помощью метода, который заставляет dicts использовать меньше памяти). Разумеется, мы можем изменить его порядок, но он побеждает.
Вместо этого я предлагаю взломать исходный код CPython, чтобы изменить способ использования хеша внутри dictobject.c. Например, после каждого hash = PyObject_Hash(key); if (hash == -1) { ..error.. };
вы можете добавить hash ^= HASH_TWEAK;
и скомпилировать разные версии CPython с разными значениями для HASH_TWEAK
. (Я делал такую вещь в какой-то момент, но я больше не могу ее найти. Вам нужно быть немного осторожным, где значения хэша являются исходными или модифицированными.)