2016-12-11 2 views
2

Мне нужно получить случайное хеш-семя, используемое питоном для репликации сбой unittests.Экстракт хеш-семени в модульном тестировании

Если PYTHONHASHSEED установлена ​​в ненулевое целое число, sys.flags.hash_randomization обеспечивает его надежно:

$ export PYTHONHASHSEED=12345 
$ python3 -c 'import sys, os;print(sys.flags.hash_randomization, os.environ.get("PYTHONHASHSEED"))' 
12345 12345 

Однако если хеширование рандомизированное, говорится только , что используется семя, не которые:

$ export PYTHONHASHSEED=random 
$ python3 -c 'import sys, os;print(sys.flags.hash_randomization, os.environ.get("PYTHONHASHSEED"))' 
1 random 

Информация в sys.hash_info никогда не включает данные в зависимости от семени. С hash function since python3.4, кажется также неосуществимым попробовать и восстановить семя от данных хешей.


Контекст: Когда тонкая настройка алгоритма, мы уже видели Heisenbugs, которые зависят от множества/Dict итерация порядка. Для их тиражирования требуется тестирование семян, в худшем - все 4294967295, но даже наше среднее число ~ 100 тестов довольно длительное.

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

ответ

6

Нет, случайное значение присваивается полю uc_Py_HashSecret union, но это никогда не подвергается действию кода Python. Это связано с тем, что количество возможных значений намного больше, чем может произвести PYTHONHASHSEED.

Если вы не установили PYTHONHASHSEED или установили его в random, Python генерирует случайное 24-байтовое значение для использования в качестве семени. Если вы задали PYTHONHASHSEED целым числом, то это число передается через linear congruential generator для получения фактического семени (см. lcg_urandom() function). Проблема в том, что PYTHONHASHSEED ограничивается только 4 байтами. Есть 256 ** в 20 раз больше возможных значений семян, чем вы могли установить только через PYTHONHASHSEED.

Вы можете доступ внутреннее значение хеш-функции в _Py_HashSecret структуры с использованием ctypes:

from ctypes import (
    c_size_t, 
    c_ubyte, 
    c_uint64, 
    pythonapi, 
    Structure, 
    Union, 
) 


class FNV(Structure): 
    _fields_ = [ 
     ('prefix', c_size_t), 
     ('suffix', c_size_t) 
    ] 


class SIPHASH(Structure): 
    _fields_ = [ 
     ('k0', c_uint64), 
     ('k1', c_uint64), 
    ] 


class DJBX33A(Structure): 
    _fields_ = [ 
     ('padding', c_ubyte * 16), 
     ('suffix', c_size_t), 
    ] 


class EXPAT(Structure): 
    _fields_ = [ 
     ('padding', c_ubyte * 16), 
     ('hashsalt', c_size_t), 
    ] 


class _Py_HashSecret_t(Union): 
    _fields_ = [ 
     # ensure 24 bytes 
     ('uc', c_ubyte * 24), 
     # two Py_hash_t for FNV 
     ('fnv', FNV), 
     # two uint64 for SipHash24 
     ('siphash', SIPHASH), 
     # a different (!) Py_hash_t for small string optimization 
     ('djbx33a', DJBX33A), 
     ('expat', EXPAT), 
    ] 


hashsecret = _Py_HashSecret_t.in_dll(pythonapi, '_Py_HashSecret') 
hashseed = bytes(hashsecret.uc) 

Однако, вы не можете на самом деле делать ничего с этой информацией. Вы не можете установить _Py_HashSecret.uc в новый процесс Python, так как это приведет к поломке большинства ключей словаря, установленных до того, как вы сможете сделать это из кода Python (внутренности Python сильно зависят от словарей), и ваши шансы на хэш равны одному из 256 ** 4 возможных значения LCG исчезающе малы.

Ваша идея установить PYTHONHASHSEED на известное значение везде является гораздо более приемлемым подходом.

+1

Глядя на код, я не думаю, что все возможные состояния 'uc' даже соответствуют значениям« PYTHONHASHSEED ». 'uc' равно 24 байтам, а' PYTHONHASHSEED' - только 4. Без 'PYTHONHASHSEED', Python инициализирует' uc' таким образом, что вряд ли какой-либо «PYTHONHASHSEED» может дать тот же результат. – user2357112

+0

@ пользователь2357112 очень хороший пункт. И вы не можете установить значение 'uc', так как к тому моменту, когда вы могли бы сделать это из программы Python, уже было бы хэшировано много ключей словаря. Установка хэш-файла 'uc' приведет к недействительности всего, что не является интернированной строкой. –

+0

Отличный ответ! Особая благодарность за дополнительную информацию о том, как фактически используется PYTHONHASHSEED. – MisterMiyagi

Смежные вопросы