Coverage for model_workflow/utils/arg_cksum.py: 91%
35 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-23 10:54 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-23 10:54 +0000
1import json
3from model_workflow.utils.auxiliar import safe_hasattr
4from model_workflow.utils.file import File
5from model_workflow.utils.selections import Selection
6from model_workflow.utils.structures import Structure
7from model_workflow.utils.register import Register
8from model_workflow.utils.cache import Cache
9from model_workflow.utils.mda_spells import get_mda_universe_cksum
10from model_workflow.utils.type_hints import *
12from MDAnalysis.core.universe import Universe
14# Note that it is more convinient not to have this function among auxiliar functions
15# We depend on class types which depend on the auxiliar module
17# Given a value of any type, generate a 'cksum' like code which is reproducible
18# This is used to compare values between different runs without having to store the whole value
19def get_cksum_id (value) -> Optional[Union[int, float, str]]:
20 # Nones remain as they are
21 if value == None: return None
22 # Ge the value type
23 value_type = type(value)
24 # For numbers simply use the number itself
25 if value_type == int or value_type == float or value_type == bool: return value
26 # For strings, sum the ordinal numbers of every letter
27 if value_type == str: return f'{sum(map(ord, value))} -> {len(value)}'
28 # For exceptions simply keep the excepction message
29 if value_type == Exception: return str(value)
30 # For objects, stringify them and then do the same that with strings
31 if value_type in { list, dict, tuple }:
32 stringifyed = json.dumps(value, default = lambda o: o.__repr__())
33 return get_cksum_id(stringifyed)
34 # If we have a set then make a list and sort it alphabetically
35 # Thus we make sure the order is coherent between different
36 if value_type == set:
37 standard = list(value).sort()
38 return get_cksum_id(standard)
39 # For functions simply store the name
40 if callable(value): return value.__name__
41 # For files use file last modification time and size
42 if isinstance(value, File): return value.get_cksum()
43 # For the parsed structure
44 if isinstance(value, Structure):
45 pdb_content = value.generate_pdb()
46 return get_cksum_id(pdb_content)
47 # For a MDAnalysis universe
48 if isinstance(value, Universe): return get_mda_universe_cksum(value)
49 # For the parsed structure
50 if isinstance(value, Selection): return f'{len(value.atom_indices)}-{sum(value.atom_indices)}'
51 # For handler class instances it makes not sense making a comparision
52 # WARNING: If they are used as input then make sure it is only being "used" and not "read"
53 # Otherwise, the content will be not compared between runs (e.g. warnings in the register)
54 if isinstance(value, Register): return True
55 if isinstance(value, Cache): return True
56 if safe_hasattr(value, '__class__'):
57 if value.__class__.__name__ == 'Project': return True
58 if value.__class__.__name__ == 'MD': return True
59 # If the value has non of previous types then we complain
60 raise TypeError(f'Non supported type "{value_type}" for cksum id: {value}')