Suppose I write a simple Python class, Delay, whose point is to encapsulate a delayed (lazy) computation:
class Delay:
def __init__(self, fn, *args, **kwargs):
self.partial = (fn, args, kwargs)
self.result = None
def __call__(self):
if self.partial is not None:
(fn, args, kwargs) = self.partial
self.result = fn(*args, **kwargs)
self.partial = None
return self.result
This is straightforward enough, but now let's think about how it will be used:
# Somewhere in some initialization module of the codebase:
def load_historical_data(filename):
with open(filename, 'r') as f:
return json.load(f)
def init_runtime(param_dict):
...
# (Prepare to) load some data that won't be needed right away.
runtime['historical_data'] = Delay(load_historical_data,
param_dict['hist_filename'])
...
return runtime
# Some far remote corner of the codebase...
def _analyze_history_job_firstpass(runtime, kwargs*):
...
histdata = runtime['historical_data']()
...
One problem with the above paradigm arises when there is a bug in the init_runtime function—for example, if the param_dict['hist_filename'] isn't a valid filename. In this case, when _analyze_history_job_firstpass eventually gets called, it will raise an exception due to the file not being found, but nothing about that exception will point back to init_runtime, which makes debugging difficult.
It would be ideal if the Delay class could detect the exception during its __call__ method and instead raise an alternate exception that documented both the call stack that created fn and the call stack that called fn. How can one achieve this? (If there are multiple ways, what are the advantages/disadvantages of each?)