Skip to content

Instantly share code, notes, and snippets.

@senko
Created November 16, 2012 11:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save senko/4086616 to your computer and use it in GitHub Desktop.
Save senko/4086616 to your computer and use it in GitHub Desktop.
sign_structure - sign nested Python structures (including dicts) while ignoring dict ordering
from hashlib import sha1
import unittest
__all__ = ['sign_structure']
def sign_structure(data):
"""Create a (non-cryptographic) signature of the data structure.
This function makes sure that dict item ordering doesn't matter.
"""
content = []
def process_dict(x):
content.append('{')
for k in sorted(x.keys()):
process(k)
content.append('=')
process(x[k])
content.append(';')
content.append('}')
def process_sequence(x, typ):
content.append('(' if typ == tuple else '[')
for el in x:
process(el)
content.append(';')
content.append(')' if typ == tuple else ']')
def process(x):
if isinstance(x, dict):
process_dict(x)
elif isinstance(x, list) or isinstance(x, tuple):
process_sequence(x, type(x))
elif isinstance(x, unicode):
content.append(u"'%s'" % x.encode('utf-8'))
elif isinstance(x, float):
content.append('%.8f;' % x)
elif isinstance(x, int):
content.append('%d;' % x)
else:
content.append("'%s';" % str(x))
process(data)
return sha1(''.join(content)).hexdigest()
class SignTest(unittest.TestCase):
def sha1(self, txt):
return sha1(txt).hexdigest()
def test_string_signing(self):
self.assertEqual(sign_structure(''),
'f3c3af94dfbad817c6a52c11882ba03102d322d6')
self.assertEqual(sign_structure('abc'),
'adab040b65c164a21f2cee29d068e37baef8cafd')
def test_unicode_signing(self):
self.assertEqual(
sign_structure('\xc5\xa0\xc4\x90\xc4\x8c\xc4\x86\xc5\xbd'),
'5d66fe7a5014f54d509aa23db1a2e8203ff9869d')
def test_integer_signing(self):
self.assertEqual(sign_structure(42),
'3ea704a51effa29961c48dbda82a85bdad323602')
def test_float_signing(self):
self.assertEqual(sign_structure(3.14159265389),
'07cab0e741f60c7a7051db9abd672b738b1420f6')
def test_list_signing(self):
self.assertEqual(sign_structure([1, 2, 3, 4, 5]),
'7b16598465b45e23a7f8bb6d187ab1b282a45185')
def test_tuple_signing(self):
self.assertEqual(sign_structure((1, 2, 3, 4, 5)),
'5549c793d5b1d44995156d20e4a97171b65adbdc')
def test_list_and_tuple_signature_differ(self):
self.assertNotEqual(sign_structure(1,), sign_structure([1]))
def test_dict_signing(self):
self.assertEqual(sign_structure({'a': 1, 'b': 2}),
'a46171bee1eca62076215398ba0d4cbe69bde70f')
def test_dict_ordering_doesnt_matter(self):
from collections import OrderedDict
a = OrderedDict()
a['foo'] = 1
a['bar'] = 2
b = OrderedDict()
b['bar'] = 2
b['foo'] = 1
self.assertNotEqual(a, b)
self.assertEqual(dict(a), dict(b))
self.assertEqual(sign_structure(a), sign_structure(b))
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment