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

1import json 

2 

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 * 

11 

12from MDAnalysis.core.universe import Universe 

13 

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 

16 

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}')