(code_obj: CodeType)
| 76 | return b"0" * len(hash_alg.digest()) |
| 77 | |
| 78 | def process(code_obj: CodeType) -> None: |
| 79 | # Recursively hash the constants that are also code objects |
| 80 | for const in code_obj.co_consts: |
| 81 | if isinstance(const, types.CodeType): |
| 82 | process(const) |
| 83 | elif isinstance(const, frozenset) and len(const) > 1: |
| 84 | # Set literals fold to frozensets whose str()/iteration order is |
| 85 | # PYTHONHASHSEED-dependent once there are 2+ elements. |
| 86 | # Sort the element reprs for a deterministic order. |
| 87 | hash_alg.update( |
| 88 | ",".join(sorted(map(repr, const))).encode("utf8") |
| 89 | ) |
| 90 | else: |
| 91 | hash_alg.update(str(const).encode("utf8")) |
| 92 | # Concatenate the names and bytecode of the current code object |
| 93 | # Will cause invalidation of variable naming at the top level |
| 94 | |
| 95 | names = [unmangle_local(name).name for name in code_obj.co_names] |
| 96 | hash_alg.update(bytes("|".join(names), "utf8")) |
| 97 | hash_alg.update(code_obj.co_code) |
| 98 | |
| 99 | process(code) |
| 100 | return hash_alg.digest() |
no test coverage detected
searching dependent graphs…