Recipe 6: Analyzing a Non-Parametric ReformΒΆ

This is an advanced recipe that should be followed only after mastering the basic recipe. This recipe shows how to customize the Calculator class so that it can analyze a tax reform that cannot be characterized using existing policy parameters (in this case a pseudo Cost-of-Living Refund reform). It uses, in a more extensive way, the object-oriented programming inheritance technique introduced in Redefining Expanded Income, so you might want to read that recipe first.

Recipe that illustrates how to customize the taxcalc Calculator class so that it can analyze a non-parametric reform (that is, a reform that cannot be represented using existing taxcalc Policy parameters.

The technique for doing this customization is standard in object-oriented programming: a child class is derived from a parent class and then customized. The derived child class inherits all the data and methods of the parent class, but can be customized by adding new data and methods or by overriding inherited methods.

The reform used to illustrate this programming technique is somewhat like the Cost-of-Living Refund, a refundable credit that was being discussed in tax policy circles during 2019 as a replacement for the EITC. But the reform analyzed here is not exactly like the Cost-of-Living Refund, so we call it a pseudo cost-of-living refund to emphasize that it is not meant to be an accurate representation of the Cost-of-Living Refund proposal.

import numpy as np
import taxcalc as tc


class Calculator(tc.Calculator):
    """
    Customized Calculator class that inherits all tc.Calculator data and
    methods, adding or overriding some to get the desired customization.
    """
    def __init__(self, policy=None, records=None, verbose=False,
                 sync_years=True, consumption=None,
                 # above are the tc.Calculator constructor arguments
                 # below is new constructor argument used in customization
                 colr_active=False):
        # call the parent class constructor
        super().__init__(policy=policy, records=records,
                         verbose=verbose, sync_years=sync_years,
                         consumption=consumption)
        # remember whether pseudo_COLR policy is active or not
        self.colr_active = colr_active
        # declare colr_param dictionary that will contain pseudo COLR policy
        self.colr_param = dict()

    def specify_pseudo_colr_policy(self):
        """
        Specify policy parameters for the COLR policy in the current_year.
        See use of these parameters below in the pseudo_colr_amount method.
        """
        # reform implementation year
        reform_year = 2020
        # specify dictionary of parameter names and values for reform_year
        self.colr_param = {
            # credit phase-in rate on earnings
            'COLR_rt': 1.0,
            # ceiling on refundable credit varies by filing-unit type, MARS
            'COLR_c': np.array([4000, 8000, 4000, 4000, 4000],
                               dtype=np.float64),
            # credit phase-out start AGI level varies by filing-unit type, MARS
            'COLR_ps': np.array([30000, 50000, 30000, 30000, 30000],
                                dtype=np.float64),
            # credit phase-out rate per dollar of AGI above COLR_ps level
            'COLR_prt': 0.2
        }
        # set pseudo COLR parameter values for current year
        this_year = self.current_year
        if self.colr_active and this_year >= reform_year:
            # set inflation-indexed values of COLR_c and COLR_ps for this year
            irates = self.__policy.inflation_rates()
            syr = tc.Policy.JSON_START_YEAR
            for name in ['COLR_c', 'COLR_ps']:
                value = self.colr_param[name]
                for year in range(reform_year, this_year):
                    value *= (1.0 + irates[year - syr])
                self.colr_param[name] = np.round(value, 2)  # to nearest penny
        else:  # if policy not active or if this year is before the reform year
            # set ceiling to zero
            self.colr_param['COLR_c'] = np.array([0.0, 0.0, 0.0, 0.0, 0.0],
                                                 dtype=np.float64)
        tracing = False  # set to True to see parameter values for this year
        if tracing:
            for name in self.colr_param:
                print('> {} {} {}'.format(
                    this_year, name, self.colr_param[name]
                ))

    def pseudo_colr_amount(self):
        """
        Calculate pseudo Cost-of-Living Refund amount.
        Note this is simply meant to illustrate a Python programming technique;
        this function does NOT calculate an exact Cost-of-Living Refund amount.
        See setting of parameters above in specify_pseudo_COLR_policy method.
        """
        recs = self.__records
        # create MARS-specific policy parameter arrays
        mars_indicators = [recs.MARS == 1, recs.MARS == 2, recs.MARS == 3,
                           recs.MARS == 4, recs.MARS == 5]
        colr_c = np.select(mars_indicators, self.colr_param['COLR_c'])
        colr_ps = np.select(mars_indicators, self.colr_param['COLR_ps'])
        colr_rt = self.colr_param['COLR_rt']
        colr_prt = self.colr_param['COLR_prt']
        # compute colr_amt
        amt_pre_phaseout = np.minimum(recs.e00200 * colr_rt, colr_c)
        phaseout = np.maximum((recs.c00100 - colr_ps) * colr_prt, 0.)
        colr_amt = np.maximum(amt_pre_phaseout - phaseout, 0.)
        setattr(recs, 'colr_amount', colr_amt)
        # reduce income and combined taxes because COLR is a refundable credit
        recs.iitax -= colr_amt
        recs.combined -= colr_amt
        # delete local arrays used only in this method
        del mars_indicators
        del colr_c
        del colr_ps
        del amt_pre_phaseout
        del phaseout
        del colr_amt

    def calc_all(self, zero_out_calc_vars=False):
        """
        Call all tax-calculation functions for the current_year.
        """
        tc.BenefitPrograms(self)
        self._calc_one_year(zero_out_calc_vars)
        tc.BenefitSurtax(self)
        tc.BenefitLimitation(self)
        tc.FairShareTax(self.__policy, self.__records)
        tc.LumpSumTax(self.__policy, self.__records)
        # specify new method to set pseudo COLR policy parameters
        self.specify_pseudo_colr_policy()  # (see above)
        # call new method to calculate pseudo COLR amount
        self.pseudo_colr_amount()  # (see above)
        tc.ExpandIncome(self.__policy, self.__records)
        tc.AfterTaxIncome(self.__policy, self.__records)

# end of customized Calculator class definition


# top-level logic of program that uses customized Calculator class

policy1 = tc.Policy()  # baseline policy is current-law policy
policy2 = tc.Policy()  # parametric reform, reformC.json, eliminates EITC

# TODO: Move to the web so this can be done standalone.
policy2.implement_reform(tc.Policy.read_json_reform('github://PSLmodels:[email protected]/docs/recipes/_static/reformC.json'))

# specify customized Calculator objects for baseline and reform:
#   baseline calc1 uses policy1 (current-law) and colr_active=False
#   reform calc2 uses policy2 (no EITC) and colr_active=True
cps_records = tc.Records.cps_constructor()
calc1 = Calculator(policy=policy1, records=cps_records, colr_active=False)
calc2 = Calculator(policy=policy2, records=cps_records, colr_active=True)

# calculate tax liabilities for years around the reform year
CYR_FIRST = 2019
CYR_LAST = 2022
for cyr in range(CYR_FIRST, CYR_LAST + 1):
    # advance to and calculate for specified cyr
    calc1.advance_to_year(cyr)
    calc1.calc_all()
    calc2.advance_to_year(cyr)
    calc2.calc_all()
    # tabulate weighted amounts
    funits = calc1.total_weight()
    itax1 = calc1.weighted_total('iitax')
    itax2 = calc2.weighted_total('iitax')
    eitc1 = calc1.weighted_total('eitc')
    eitc2 = calc2.weighted_total('eitc')
    colr1 = calc1.weighted_total('colr_amount')
    colr2 = calc2.weighted_total('colr_amount')
    # print weighted amounts for cyr
    if cyr == CYR_FIRST:
        print('YEAR  UNITS   ITAX1   ITAX2  EITC1  EITC2  COLR1  COLR2')
    line = '{}  {:.1f}  {:.1f}  {:.1f}  {:5.1f}  {:5.1f}  {:5.1f}  {:5.1f}'
    print(line.format(cyr, funits * 1e-6,
                      itax1 * 1e-9, itax2 * 1e-9,
                      eitc1 * 1e-9, eitc2 * 1e-9,
                      colr1 * 1e-9, colr2 * 1e-9))
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-2-ecde50f9b276> in <module>
    136     # advance to and calculate for specified cyr
    137     calc1.advance_to_year(cyr)
--> 138     calc1.calc_all()
    139     calc2.advance_to_year(cyr)
    140     calc2.calc_all()

<ipython-input-2-ecde50f9b276> in calc_all(self, zero_out_calc_vars)
    100         """
    101         tc.BenefitPrograms(self)
--> 102         self._calc_one_year(zero_out_calc_vars)
    103         tc.BenefitSurtax(self)
    104         tc.BenefitLimitation(self)

~/work/Tax-Calculator/Tax-Calculator/taxcalc/calculator.py in _calc_one_year(self, zero_out_calc_vars)
   1441         CharityCredit(self.__policy, self.__records)
   1442         ChildDepTaxCredit(self.__policy, self.__records)
-> 1443         NonrefundableCredits(self.__policy, self.__records)
   1444         AdditionalCTC(self.__policy, self.__records)
   1445         C1040(self.__policy, self.__records)

~/work/Tax-Calculator/Tax-Calculator/taxcalc/decorators.py in wrapper(*args, **kwargs)
    323                  {"applied_f": applied_jitted_f}, fakeglobals)
    324             high_level_fn = fakeglobals['hl_func']
--> 325             ans = high_level_fn(*args, **kwargs)
    326             return ans
    327 

<string> in hl_func(pm, pf)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_for_args(self, *args, **kws)
    370         return_val = None
    371         try:
--> 372             return_val = self.compile(tuple(argtypes))
    373         except errors.ForceLiteralArg as e:
    374             # Received request for compiler re-entry with the list of arguments

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, sig)
    907                 with ev.trigger_event("numba:compile", data=ev_details):
    908                     try:
--> 909                         cres = self._compiler.compile(args, return_type)
    910                     except errors.ForceLiteralArg as e:
    911                         def folded(args, kws):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, args, return_type)
     77 
     78     def compile(self, args, return_type):
---> 79         status, retval = self._compile_cached(args, return_type)
     80         if status:
     81             return retval

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_cached(self, args, return_type)
     91 
     92         try:
---> 93             retval = self._compile_core(args, return_type)
     94         except errors.TypingError as e:
     95             self._failed_cache[key] = e

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_core(self, args, return_type)
    104 
    105         impl = self._get_implementation(args, {})
--> 106         cres = compiler.compile_extra(self.targetdescr.typing_context,
    107                                       self.targetdescr.target_context,
    108                                       impl,

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
    604     pipeline = pipeline_class(typingctx, targetctx, library,
    605                               args, return_type, flags, locals)
--> 606     return pipeline.compile_extra(func)
    607 
    608 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(self, func)
    351         self.state.lifted = ()
    352         self.state.lifted_from = None
--> 353         return self._compile_bytecode()
    354 
    355     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_bytecode(self)
    413         """
    414         assert self.state.func_ir is None
--> 415         return self._compile_core()
    416 
    417     def _compile_ir(self):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_core(self)
    384             res = None
    385             try:
--> 386                 pm.run(self.state)
    387                 if self.state.cr is not None:
    388                     break

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in run(self, state)
    328                 pass_inst = _pass_registry.get(pss).pass_inst
    329                 if isinstance(pass_inst, CompilerPass):
--> 330                     self._runPass(idx, pass_inst, state)
    331                 else:
    332                     raise BaseException("Legacy pass in use")

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     33         def _acquire_compile_lock(*args, **kwargs):
     34             with self:
---> 35                 return func(*args, **kwargs)
     36         return _acquire_compile_lock
     37 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    287             mutated |= check(pss.run_initialization, internal_state)
    288         with SimpleTimer() as pass_time:
--> 289             mutated |= check(pss.run_pass, internal_state)
    290         with SimpleTimer() as finalize_time:
    291             mutated |= check(pss.run_finalizer, internal_state)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in check(func, compiler_state)
    260 
    261         def check(func, compiler_state):
--> 262             mangled = func(compiler_state)
    263             if mangled not in (True, False):
    264                 msg = ("CompilerPass implementations should return True/False. "

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    102                               % (state.func_id.func_name,)):
    103             # Type inference
--> 104             typemap, return_type, calltypes, errs = type_inference_stage(
    105                 state.typingctx,
    106                 state.func_ir,

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in type_inference_stage(typingctx, interp, args, return_type, locals, raise_errors)
     80         infer.build_constraint()
     81         # return errors in case of partial typing
---> 82         errs = infer.propagate(raise_errors=raise_errors)
     83         typemap, restype, calltypes = infer.unify(raise_errors=raise_errors)
     84 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typeinfer.py in propagate(self, raise_errors)
   1061             # Errors can appear when the type set is incomplete; only
   1062             # raise them when there is no progress anymore.
-> 1063             errors = self.constraints.propagate(self)
   1064             newtoken = self.get_state_token()
   1065             self.debug.propagate_finished()

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typeinfer.py in propagate(self, typeinfer)
    152                                                    lineno=loc.line):
    153                 try:
--> 154                     constraint(typeinfer)
    155                 except ForceLiteralArg as e:
    156                     errors.append(e)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typeinfer.py in __call__(self, typeinfer)
    564                 fnty = typevars[self.func].getone()
    565             with new_error_context("resolving callee type: {0}", fnty):
--> 566                 self.resolve(typeinfer, typevars, fnty)
    567 
    568     def resolve(self, typeinfer, typevars, fnty):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typeinfer.py in resolve(self, typeinfer, typevars, fnty)
    584         # Resolve call type
    585         try:
--> 586             sig = typeinfer.resolve_call(fnty, pos_args, kw_args)
    587         except ForceLiteralArg as e:
    588             # Adjust for bound methods

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typeinfer.py in resolve_call(self, fnty, pos_args, kw_args)
   1536         else:
   1537             # Normal non-recursive call
-> 1538             return self.context.resolve_function_type(fnty, pos_args, kw_args)
   1539 
   1540     def typeof_global(self, inst, target, gvar):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typing/context.py in resolve_function_type(self, func, args, kws)
    192         # Prefer user definition first
    193         try:
--> 194             res = self._resolve_user_function_type(func, args, kws)
    195         except errors.TypingError as e:
    196             # Capture any typing error

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typing/context.py in _resolve_user_function_type(self, func, args, kws, literals)
    244         if isinstance(func, types.Callable):
    245             # XXX fold this into the __call__ attribute logic?
--> 246             return func.get_call_type(self, args, kws)
    247 
    248     def _get_attribute_templates(self, typ):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/types/functions.py in get_call_type(self, context, args, kws)
    526         """
    527         template, pysig, args, kws = \
--> 528             self.dispatcher.get_call_template(args, kws)
    529         sig = template(context).apply(args, kws)
    530         if sig:

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in get_call_template(self, args, kws)
    313         # Ensure an overload is available
    314         if self._can_compile:
--> 315             self.compile(tuple(args))
    316 
    317         # Create function type for typing

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, sig)
    907                 with ev.trigger_event("numba:compile", data=ev_details):
    908                     try:
--> 909                         cres = self._compiler.compile(args, return_type)
    910                     except errors.ForceLiteralArg as e:
    911                         def folded(args, kws):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in compile(self, args, return_type)
     77 
     78     def compile(self, args, return_type):
---> 79         status, retval = self._compile_cached(args, return_type)
     80         if status:
     81             return retval

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_cached(self, args, return_type)
     91 
     92         try:
---> 93             retval = self._compile_core(args, return_type)
     94         except errors.TypingError as e:
     95             self._failed_cache[key] = e

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/dispatcher.py in _compile_core(self, args, return_type)
    104 
    105         impl = self._get_implementation(args, {})
--> 106         cres = compiler.compile_extra(self.targetdescr.typing_context,
    107                                       self.targetdescr.target_context,
    108                                       impl,

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(typingctx, targetctx, func, args, return_type, flags, locals, library, pipeline_class)
    604     pipeline = pipeline_class(typingctx, targetctx, library,
    605                               args, return_type, flags, locals)
--> 606     return pipeline.compile_extra(func)
    607 
    608 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in compile_extra(self, func)
    351         self.state.lifted = ()
    352         self.state.lifted_from = None
--> 353         return self._compile_bytecode()
    354 
    355     def compile_ir(self, func_ir, lifted=(), lifted_from=None):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_bytecode(self)
    413         """
    414         assert self.state.func_ir is None
--> 415         return self._compile_core()
    416 
    417     def _compile_ir(self):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler.py in _compile_core(self)
    384             res = None
    385             try:
--> 386                 pm.run(self.state)
    387                 if self.state.cr is not None:
    388                     break

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in run(self, state)
    328                 pass_inst = _pass_registry.get(pss).pass_inst
    329                 if isinstance(pass_inst, CompilerPass):
--> 330                     self._runPass(idx, pass_inst, state)
    331                 else:
    332                     raise BaseException("Legacy pass in use")

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_lock.py in _acquire_compile_lock(*args, **kwargs)
     33         def _acquire_compile_lock(*args, **kwargs):
     34             with self:
---> 35                 return func(*args, **kwargs)
     36         return _acquire_compile_lock
     37 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in _runPass(self, index, pss, internal_state)
    287             mutated |= check(pss.run_initialization, internal_state)
    288         with SimpleTimer() as pass_time:
--> 289             mutated |= check(pss.run_pass, internal_state)
    290         with SimpleTimer() as finalize_time:
    291             mutated |= check(pss.run_finalizer, internal_state)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/compiler_machinery.py in check(func, compiler_state)
    260 
    261         def check(func, compiler_state):
--> 262             mangled = func(compiler_state)
    263             if mangled not in (True, False):
    264                 msg = ("CompilerPass implementations should return True/False. "

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    461 
    462         # TODO: Pull this out into the pipeline
--> 463         NativeLowering().run_pass(state)
    464         lowered = state['cr']
    465         signature = typing.signature(state.return_type, *state.args)

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/typed_passes.py in run_pass(self, state)
    409             else:
    410                 # Prepare for execution
--> 411                 cfunc = targetctx.get_executable(library, fndesc, env)
    412                 # Insert native function for use by other jitted-functions.
    413                 # We also register its library to allow for inlining.

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/cpu.py in get_executable(self, library, fndesc, env)
    209         """
    210         # Code generation
--> 211         baseptr = library.get_pointer_to_function(fndesc.llvm_func_name)
    212         fnptr = library.get_pointer_to_function(fndesc.llvm_cpython_wrapper_name)
    213 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/codegen.py in get_pointer_to_function(self, name)
    958             - non-zero if the symbol is defined.
    959         """
--> 960         self._ensure_finalized()
    961         ee = self._codegen._engine
    962         if not ee.is_symbol_defined(name):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/codegen.py in _ensure_finalized(self)
    566     def _ensure_finalized(self):
    567         if not self._finalized:
--> 568             self.finalize()
    569 
    570     def _optimize_functions(self, ll_module):

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/codegen.py in finalize(self)
    699         # Optimize the module after all dependences are linked in above,
    700         # to allow for inlining.
--> 701         self._optimize_final_module()
    702 
    703         self._final_module.verify()

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/numba/core/codegen.py in _optimize_final_module(self)
    592             # A cheaper optimisation pass is run first to try and get as many
    593             # refops into the same function as possible via inlining
--> 594             self._codegen._mpm_cheap.run(self._final_module)
    595         # Refop pruning is then run on the heavily inlined function
    596         if not config.LLVM_REFPRUNE_PASS:

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/llvmlite/binding/passmanagers.py in run(self, module)
    205         Run optimization passes on the given module.
    206         """
--> 207         return ffi.lib.LLVMPY_RunPassManager(self, module)
    208 
    209 

/usr/share/miniconda/envs/taxcalc-dev/lib/python3.8/site-packages/llvmlite/binding/ffi.py in __call__(self, *args, **kwargs)
    149     def __call__(self, *args, **kwargs):
    150         with self._lock:
--> 151             return self._cfn(*args, **kwargs)
    152 
    153 

KeyboardInterrupt: