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
« 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
6import cloudpickle # type: ignore # no stubs available 1a
8from prefect.exceptions import HashError 1a
9from prefect.serializers import JSONSerializer 1a
11_md5 = partial(hashlib.md5, usedforsecurity=False) 1a
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.
17 Supports bytes and strings. Strings will be UTF-8 encoded.
19 Args:
20 *args: Items to include in the hash.
21 hash_algo: Hash algorithm from hashlib to use.
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
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.
37 Args:
38 path (str): the path to a file
39 hash_algo: Hash algorithm from hashlib to use.
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
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.
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
63 Returns:
64 A hash string or None if hashing failed
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
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
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)
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)
91 return None