2016-12-11 1 views
2

私は、が失敗する unittestsを複製するためにpythonで使用されるランダムなハッシュシードを取得する必要があります。 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では、与えられたハッシュからシードを再構築しようとすることも実行不可能です。


コンテキスト:アルゴリズムを微調整するとき、set/dictの反復順序に依存するheisenbugを見てきました。それらを複製するには、最悪の場合は4294967295の種をテストする必要がありますが、私たちの平均でも〜100回のテストでさえかなり時間がかかります。

PYTHONHASHSEEDはランダムであるが既知の値に常に設定していますが、この余分なレイヤーは避けたいと考えています。

答えて

6

いいえ、ランダム値は_Py_HashSecret unionucフィールドに割り当てられますが、これは決してPythonコードに公開されません。これは、可能な値の数が、PYTHONHASHSEEDが生成できる設定値よりもはるかに大きいためです。

PYTHONHASHSEEDを設定しないか、randomに設定すると、Pythonはランダムな24バイト値を生成してシードとして使用します。 PYTHONHASHSEEDを整数に設定すると、その数値はlinear congruential generatorに渡され、実際のシードが生成されます(lcg_urandom() functionを参照)。問題はPYTHONHASHSEEDが4バイトに限定されていることです。 PYTHONHASHSEEDだけで設定可能な値よりも256 ** 20倍多くの可能なシード値があります。

あなたできアクセス​​を使用して_Py_HashSecret構造体の内部ハッシュ値:

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) 

しかし、あなたが実際にがこの情報を何もすることができません。新しいPythonプロセスで_Py_HashSecret.ucを設定することはできません。そうすることで、Pythonコード(Python内部が辞書に大きく依存しています)から実行できるようになる前に設定されているほとんどの辞書キーが破損し、ハッシュが256 **可能な4つのLCG値は、はるかに小さいです。

どこでも既知の値にPYTHONHASHSEEDを設定するという考え方ははるかに実現可能なアプローチです。

+1

コードを見ると、私は 'uc'のすべての可能な状態が' PYTHONHASHSEED'値に対応するとは思いません。 'uc'は24バイトですが、' PYTHONHASHSEED'は4だけです。 'PYTHONHASHSEED'がないと、Pythonは' PYTHONHASHSEED'が同じ結果を生み出すことはないように 'uc'を初期化します。 – user2357112

+0

@ user2357112非常に良い点があります。そしてあなたは 'uc'値を設定することもできません。Pythonプログラムからそうすることができる時代には、既にたくさんの辞書キーがハッシュされていたはずです。 'uc'ハッシュシードを設定すると、中身の文字列ではないものは無効になります。 –

+0

偉大な答え! 'PYTHONHASHSEED'が実際にどのように使われているかについての特別な情報をありがとうございます。 – MisterMiyagi

関連する問題