我目前正在编写装饰器来将结果缓存在磁盘上。我希望无论 f(x) 是否已存储,f(x) 都会给出相同的结果,即使 f(x) 引发异常。即,当 f 引发异常时加载 f(x) 的存储结果应该引发相同的异常(稍微修改指示上下文)。
我已经成功编写了 f 存储在内存中的代码,但是在使用 pickle (将用于保存在磁盘上)时它不起作用。这是完整的演示代码。请注意,当异常经过 pickle 后,它如何不输出回溯。
import pickle, traceback, sys
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def buggy(x):
raise ValueError("my error")
def f(x):
if x < 10:
return buggy(x)
else:
return x+2
class DelayedException(Exception):
def __init__(self, s):
super().__init__(s)
for val, method in [
(0, "no_cache"), (100, "no_cache"), #direct function call (what we want to imitate)
(0, "memory_cached"), (100, "memory_cached"), #reraising later without saving/loading (currently good result)
(0, "pickled_cache"), (100, "pickled_cache") #reraising later with saving/loading in between
]:
print("\n\n")
try:
logger.info(f"val = {val}, method={method}")
if method=="no_cache":
res = f(val)
else:
try:
res = f(val)
except BaseException as e:
# The code can be changed here
try:
#Not very readable technique to append this exception in the exception stack.
#I'm very open to samething better
raise DelayedException(f"Error in {f.__name__}({val})")
except DelayedException as d:
try:
raise e from d
except BaseException as final:
res = final
if method=="pickled_cache":
#for simplicity we use a string instead of a file for demo purposes
res = pickle.dumps(res)
res = pickle.loads(res)
if isinstance(res, BaseException):
# The code also can be changed here
raise res
logger.info(f"ok, res = {res}")
except:
logger.error(traceback.format_exc())
关于如何让它发挥作用有什么想法吗?代码中的注释应该让我知道我想在哪里更改内容。
另外,我已经研究过