Coverage for /usr/local/lib/python3.12/site-packages/prefect/utilities/hashing.py: 75%

34 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 13:38 +0000

1import hashlib 1a

2from functools import partial 1a

3from pathlib import Path 1a

4from typing import Any, Callable, Optional, Union 1a

5 

6import cloudpickle # type: ignore # no stubs available 1a

7 

8from prefect.exceptions import HashError 1a

9from prefect.serializers import JSONSerializer 1a

10 

11_md5 = partial(hashlib.md5, usedforsecurity=False) 1a

12 

13 

14def stable_hash(*args: Union[str, bytes], hash_algo: Callable[..., Any] = _md5) -> str: 1a

15 """Given some arguments, produces a stable 64-bit hash of their contents. 

16 

17 Supports bytes and strings. Strings will be UTF-8 encoded. 

18 

19 Args: 

20 *args: Items to include in the hash. 

21 hash_algo: Hash algorithm from hashlib to use. 

22 

23 Returns: 

24 A hex hash. 

25 """ 

26 h = hash_algo() 1ab

27 for a in args: 1ab

28 if isinstance(a, str): 28 ↛ 29line 28 didn't jump to line 29 because the condition on line 28 was never true1ab

29 a = a.encode() 

30 h.update(a) 1ab

31 return h.hexdigest() 1ab

32 

33 

34def file_hash(path: str, hash_algo: Callable[..., Any] = _md5) -> str: 1a

35 """Given a path to a file, produces a stable hash of the file contents. 

36 

37 Args: 

38 path (str): the path to a file 

39 hash_algo: Hash algorithm from hashlib to use. 

40 

41 Returns: 

42 str: a hash of the file contents 

43 """ 

44 contents = Path(path).read_bytes() 1a

45 return stable_hash(contents, hash_algo=hash_algo) 1a

46 

47 

48def hash_objects( 1a

49 *args: Any, 

50 hash_algo: Callable[..., Any] = _md5, 

51 raise_on_failure: bool = False, 

52 **kwargs: Any, 

53) -> Optional[str]: 

54 """ 

55 Attempt to hash objects by dumping to JSON or serializing with cloudpickle. 

56 

57 Args: 

58 *args: Positional arguments to hash 

59 hash_algo: Hash algorithm to use 

60 raise_on_failure: If True, raise exceptions instead of returning None 

61 **kwargs: Keyword arguments to hash 

62 

63 Returns: 

64 A hash string or None if hashing failed 

65 

66 Raises: 

67 HashError: If objects cannot be hashed and raise_on_failure is True 

68 """ 

69 json_error = None 1ab

70 pickle_error = None 1ab

71 

72 try: 1ab

73 serializer = JSONSerializer(dumps_kwargs={"sort_keys": True}) 1ab

74 return stable_hash(serializer.dumps((args, kwargs)), hash_algo=hash_algo) 1ab

75 except Exception as e: 1ab

76 json_error = str(e) 1ab

77 

78 try: 1ab

79 return stable_hash(cloudpickle.dumps((args, kwargs)), hash_algo=hash_algo) # type: ignore[reportUnknownMemberType] 1ab

80 except Exception as e: 

81 pickle_error = str(e) 

82 

83 if raise_on_failure: 

84 msg = ( 

85 "Unable to create hash - objects could not be serialized.\n" 

86 f" JSON error: {json_error}\n" 

87 f" Pickle error: {pickle_error}" 

88 ) 

89 raise HashError(msg) 

90 

91 return None