def option_getter(config_model, data):
    o = config_model
    d = data

    def get_option(option, x=None, default=None, ignore_inheritance=False):

        def _get_option(opt, fail=False):
            try:
                result = o.get_key('techs.' + opt)
            except KeyError:
                if ignore_inheritance:
                    return _get_option(default, fail)
                # 'ccgt.constraints.s_time' -> 'ccgt', 'constraints.s_time'
                tech, remainder = opt.split('.', 1)
                if ':' in tech:
                    parent = tech.split(':')[0]
                else:
                    # parent = e.g. 'defaults'
                    parent = o.get_key('techs.' + tech + '.parent')
                try:
                    result = _get_option(parent + '.' + remainder, fail)
                except KeyError:
                    e = exceptions.OptionNotSetError
                    if fail:
                        raise e('Failed to read option `{}` '
                                'with given default '
                                '`{}`'.format(option, default))
                    elif default:
                        result = _get_option(default, fail=True)
                    elif tech == 'defaults':
                        raise e('Reached top of inheritance chain '
                                'and no default defined for: '
                                '`{}`'.format(option))
                    else:
                        raise e('Can not get parent for `{}` '
                                'and no default defined '
                                '({}).'.format(tech, option))
            return result

        def _get_location_option(key, location):

            def getter(key, location):
                # NB: KeyErrors raised here are always caught within _get_option
                # so need no further information or handling
                # Raises KeyError if the specific _override column does not exist
                result = d.locations.at[location, '_override.' + key]
                # Also raise KeyError if the result is NaN, i.e. if no
                # location-specific override has been defined
                try:
                    if np.isnan(result):
                        raise KeyError
                # Have to catch this because np.isnan not implemented for strings
                except TypeError:
                    pass
                return result

            while True:
                try:
                    return getter(key, location)
                except KeyError:
                    parent_location = d.locations.at[location, '_within']
                    if parent_location:  # Will be None if no parent
                        return getter(key, parent_location)
                    else:
                        # Once top of "location inheritance" chain reached,
                        # raise KeyError, which will cause the calling function
                        # to fall back to non-location specific settings
                        raise

        if x:
            try:
                result = _get_location_option(option, x)
            # If can't find a location-specific option, fall back to model-wide
            except KeyError:
                result = _get_option(option)
        else:
            result = _get_option(option)
        # Deal with 'inf' settings
        if result == 'inf':
            result = float('inf')
        return result

    return get_option
Returns a get_option() function using the given config_model and data
dataset/ETHPy150Open calliope-project/calliope/calliope/utils.py/option_getter