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?)