Source code for pypeit.par.util

# -*- coding: utf-8 -*-
"""
Utility functions for PypeIt parameter sets

.. include:: ../include/links.rst
"""
import ast
import itertools
from typing import Type

from configobj import ConfigObj
from IPython import embed

from pypeit import PypeItError


[docs] def _eval_ignore(): """Provides a list of strings that should not be evaluated.""" return [ 'open', 'file', 'dict', 'list', 'tuple' ]
[docs] def ast_literal_eval(inp): """ A wrapper for :func:`ast.literal_eval` that returns the input if it raises a ValueError. """ try: return ast.literal_eval(inp) except ValueError: return inp
[docs] def _eval_iter(inp:list[str], left:str, right:str, otype:Type) -> list: """ Convenience function used to abstract the core functionality of :func:`eval_tuple` and :func:`eval_list`. Parameters ---------- inp Input list of strings left Left bracket delimeter right Right bracket delimeter otype Return type for the components of the list Returns ------- A list of objects with type ``otype`` as converted from the provided strings. """ grps = [ i for i in list(itertools.chain(*[s.split(right) for s in ','.join(inp).split(left)])) if len(i) > 1 ] return list(otype(map(ast_literal_eval, g.split(','))) for g in grps)
[docs] def eval_tuple(inp:list[str]) -> list[tuple]: """ Evaluate the input to one or more tuples. This allows conversion of one or more tuples provided to a configuration parameters. .. warning:: - Currently can only handle tuples with integers, floats, or strings! Parameters ---------- inp A list of strings that are converted into a list of tuples. The parentheses must be within the list of elements. Returns ------- A list of tuples with the converted elements. """ return _eval_iter(inp, '(', ')', tuple)
[docs] def eval_list(inp:list[str]) -> list[list]: """ Evaluate the input to one or more lists. This allows conversion of one or more lists provided to a configuration parameters. .. warning:: - Currently can only handle lists with integers, floats, or strings! Parameters ---------- inp A list of strings that are converted into a list of lists. The square brackets must be within the list of elements. Returns ------- A list of lists with the converted elements. """ return _eval_iter(inp, '[', ']', list)
[docs] def recursive_dict_evaluate(d): """ Recursively run :func:`eval` on each element of the provided dictionary. A raw read of a configuration file with `configobj`_ results in a dictionary that contains strings or lists of strings. However, when assigning the values for the various ParSets, the `from_dict` methods expect the dictionary values to have the appropriate type. E.g., the `configobj`_ will have something like d['foo'] = '1', when the `from_dict` method expects the value to be an integer (d['foo'] = 1). This function tries to evaluate *all* dictionary values, except for those listed above in the :func:`_eval_ignore` function. Any value in this list or where:: ast_literal_eval(d[k]) for k in d.keys() raises an exception is returned as the original string. This is currently only used in :func:`~pypeit.par.pypeitpar.PypitPar.from_cfg_file`; see further comments there. Args: d (dict): Dictionary of values to evaluate Returns: dict: Identical to input dictionary, but with all string values replaced with the result of `eval(d[k])` for all `k` in `d.keys()`. """ ignore = _eval_ignore() for k in d.keys(): if isinstance(d[k], dict): # Recursive call to deal with nested dictionaries d[k] = recursive_dict_evaluate(d[k]) continue if isinstance(d[k], list) and any(['(' in e for e in d[k]]): # NOTE: This enables syntax for constructing one or more tuples. try: d[k] = eval_tuple(d[k]) except (PypeItError, SyntaxError) as e: # The tuple evaluation failed. Assume that this can be handled # later in the code and leave the dictionary element unaltered. # # SyntaxError is raised for entries that include a tuple for the # mosaic and a series of locations in the mosaiced image, like # add_slits, rm_slits, and manual. pass continue if isinstance(d[k], list): replacement = [] for v in d[k]: if v in ignore: replacement += [ v ] else: try: replacement += [ ast_literal_eval(v) ] except: replacement += [ v ] d[k] = replacement continue try: d[k] = ast_literal_eval(d[k]) if d[k] not in ignore else d[k] except: pass return d
[docs] def parset_to_dict(par): """ Convert the provided parset into a dictionary. Args: par (ParSet): Returns: dict: Converted ParSet """ try: d = dict(ConfigObj(par.to_config(section_name='tmp'))['tmp']) except: d = dict(ConfigObj(par.to_config())) return recursive_dict_evaluate(d)