diff --git a/pangeo_forge_recipes/serialization.py b/pangeo_forge_recipes/serialization.py index e5eb30c6f..a44d5424c 100644 --- a/pangeo_forge_recipes/serialization.py +++ b/pangeo_forge_recipes/serialization.py @@ -4,6 +4,9 @@ from enum import Enum from hashlib import sha256 from json import dumps +import dis +import io +from contextlib import redirect_stdout from typing import Any, List, Sequence @@ -19,7 +22,24 @@ def either_encode_or_hash(obj: Any): elif hasattr(obj, "sha256"): return obj.sha256().hex() elif inspect.isfunction(obj): - return inspect.getsource(obj) + try: + return inspect.getsource(obj) + except OSError as e: + # If the function is coming from something that was `exec`d (as pangeo-forge-runner is) + # or a lambda, inspect.getsource doesn't work. In that case, we construct something as + # stable as possible. However, this is *not* guaranteed to be stable across major + # python versions. But I *think* that is fine? + if e.args == ('could not get source code',): + codeobj = obj.__code__ + # Name of + name = codeobj.co_name + args = codeobj.co_varnames[:codeobj.co_argcount] + # This is not guaranteed to be stable across python versions unfortunately + body = codeobj.co_code + return f"{name}({', '.join(args)}): {body}" + with redirect_stdout(io.StringIO()) as f: + dis.dis(obj) + return obj.__name__ + "-n" + f.getvalue() raise TypeError(f"object of type {type(obj).__name__} not serializable")