Skip to content

Commit b6cd0cb

Browse files
Fix hashing (libAtoms#121)
* Test hashing * Fix Hasher * Split up hash tests
1 parent 4f1b0f6 commit b6cd0cb

File tree

2 files changed

+78
-14
lines changed

2 files changed

+78
-14
lines changed

abcd/model.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313

1414
class Hasher(object):
15-
def __init__(self, method=md5()):
16-
self.method = method
15+
def __init__(self, method=md5):
16+
self.method = method()
1717

1818
def update(self, value):
1919

@@ -273,26 +273,25 @@ def pre_save(self):
273273
self["username"] = getpass.getuser()
274274

275275
if not self.get("uploaded"):
276-
self["uploaded"] = datetime.datetime.utcnow()
276+
self["uploaded"] = datetime.datetime.now(datetime.timezone.utc)
277277

278-
self["modified"] = datetime.datetime.utcnow()
278+
self["modified"] = datetime.datetime.now(datetime.timezone.utc)
279279

280-
m = Hasher()
280+
hasher = Hasher()
281281

282282
for key in ("numbers", "positions", "cell", "pbc"):
283-
m.update(self[key])
283+
hasher.update(self[key])
284284

285285
self.derived_keys.append("hash_structure")
286-
self["hash_structure"] = m()
286+
self["hash_structure"] = hasher()
287287

288-
m = Hasher()
289288
for key in self.arrays_keys:
290-
m.update(self[key])
289+
hasher.update(self[key])
291290
for key in self.info_keys:
292-
m.update(self[key])
291+
hasher.update(self[key])
293292

294293
self.derived_keys.append("hash")
295-
self["hash"] = m()
294+
self["hash"] = hasher()
296295

297296

298297
if __name__ == "__main__":

tests/test_abstract_model.py

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import io
23

34
import ase
@@ -8,7 +9,7 @@
89
from ase.io import read, write
910
import numpy as np
1011

11-
from abcd.model import AbstractModel
12+
from abcd.model import AbstractModel, Hasher
1213
from ase.calculators.lj import LennardJones
1314

1415

@@ -238,7 +239,6 @@ def test_write_and_read(store_calc):
238239
"hash",
239240
"modified",
240241
"uploaded",
241-
"hash_structure", # see issue #118
242242
}:
243243
assert (
244244
abcd_data[key] == abcd_data_after_read[key]
@@ -247,10 +247,75 @@ def test_write_and_read(store_calc):
247247
# expected differences - n.b. order of calls above
248248
assert abcd_data_after_read["modified"] > abcd_data["modified"]
249249
assert abcd_data_after_read["uploaded"] > abcd_data["uploaded"]
250-
assert abcd_data_after_read["hash"] != abcd_data["hash"]
251250

252251
# expect results to match within fp precision
253252
for key in set(abcd_data.results_keys):
254253
assert abcd_data[key] == approx(
255254
np.array(abcd_data_after_read[key])
256255
), f"{key}'s value does not match"
256+
257+
258+
def test_hash_update():
259+
"""Test hash can be updated after initialisation."""
260+
hasher_1 = Hasher()
261+
262+
init_hash = hasher_1()
263+
hasher_1.update("Test value")
264+
assert hasher_1() != init_hash
265+
266+
267+
@pytest.mark.parametrize(
268+
"data",
269+
[
270+
1296,
271+
3.14,
272+
[1, 2, 3],
273+
(4, 5, 6),
274+
{"a": "value"},
275+
datetime.datetime.now(datetime.timezone.utc),
276+
b"test",
277+
],
278+
)
279+
def test_hash_data_types(data):
280+
"""Test updating hash for different data types."""
281+
hasher_1 = Hasher()
282+
hasher_1.update("Test value")
283+
updated_hash = hasher_1()
284+
285+
hasher_1.update(data)
286+
assert updated_hash != hasher_1()
287+
288+
289+
def test_second_hash_init():
290+
"""Test second hash is initialised correctly."""
291+
hasher_1 = Hasher()
292+
293+
init_hash = hasher_1()
294+
hasher_1.update("Test value")
295+
296+
hasher_2 = Hasher()
297+
assert hasher_2() == init_hash
298+
299+
300+
@pytest.mark.parametrize(
301+
"data",
302+
[
303+
1296,
304+
3.14,
305+
[1, 2, 3],
306+
(4, 5, 6),
307+
{"a": "value"},
308+
datetime.datetime.now(datetime.timezone.utc),
309+
b"test",
310+
],
311+
)
312+
def test_consistent_hash(data):
313+
"""Test two hashers agree with same data."""
314+
hasher_1 = Hasher()
315+
hasher_1.update("Test value")
316+
hasher_1.update(data)
317+
318+
hasher_2 = Hasher()
319+
hasher_2.update("Test value")
320+
hasher_2.update(data)
321+
assert hasher_1() == hasher_2()

0 commit comments

Comments
 (0)