Source code for mangadap.util.parser

# Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""

Provides a set of parsing utility functions.

.. todo::
    - Add function that will parse the default MaNGA fits file name (see
      :func:`mangadap.config.defaults.manga_fits_root`).

----

.. include license and copyright
.. include:: ../include/copy.rst

----

.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst
"""
from pathlib import Path
from os import environ
import warnings
from IPython import embed
from configparser import ConfigParser, ExtendedInterpolation

[docs]def arginp_to_list(inp, evaluate=False, quiet=True): """ Separate a list of comma-separated values in the input string to a :obj:`list`. Args: inp (:obj:`str`, :obj:`list`): Input string with a list of comma-separated values evaluate (:obj:`bool`, optional): Attempt to evaluate the elements in the list using `eval`_. quiet (:obj:`bool`, optional): Suppress terminal output. Returns: :obj:`list`: The list of the comma-separated values """ out = inp # Simply return a None value if out is None: return out # If the input value is a string, convert it to a list of strings if isinstance(out, str): out = out.replace("[", "") out = out.replace("]", "") out = out.replace(" ", "") out = out.split(',') # If the input is still not a list, make it one if not isinstance(out, list): out = [out] # Evaluate each string, if requested if evaluate: n = len(out) for i in range(0,n): try: tmp = eval(out[i]) except (NameError, TypeError) as e: if not quiet: warnings.warn(f'Could not evaluate value {out[i]}. Skipping.') else: out[i] = tmp return out
# TODO: Replace with ', '.join([str(f) for f in flist])
[docs]def list_to_csl_string(flist): """ Convert a list to a comma-separated string; i.e. perform the inverse of :func:`arginp_to_list`. Args: flist (:obj:`list`): List to convert to a comma-separated string Returns: :obj:`str`: String with the values in the input list converted to strings separated by commas """ n = len(flist) out=str(flist[0]) for i in range(1,n): out += ', '+str(flist[i]) return out
[docs]def parse_drp_file_name(name): """ Parse the name of a DRP file to provide the plate, ifudesign, and mode. Args: name (str): Name of the DRP file. Returns: int, int, str: The plate, ifudesign, and mode ('RSS' or 'CUBE') of the DRP file pulled from the file name. Raises: TypeError: Raised if *name* is not a string. ValueError: Raised if if the file name does not look like a DRP file because it does not include 'manga-', '-LOG', or '.fits.gz'. """ if (type(name) != str): raise TypeError("File name must be a string.") if (str.find(name, 'manga-') == -1 or str.find(name, '-LOG') == -1 or str.find(name, '.fits.gz') == -1): raise ValueError("String does not look like a DRP fits file name.") plate_start = str.find(name, '-')+1 plate_end = str.find(name,'-',plate_start) plate = long(name[plate_start:plate_end]) ifudesign_end = str.find(name,'-',plate_end+1) ifudesign = long(name[plate_end+1:ifudesign_end]) mode_start = str.find(name,'LOG')+3 mode = name[mode_start:str.find(name,'.fits.gz')] return plate, ifudesign, mode
#def parse_dap_file_name(name): # """ # Parse the name of a DAP file and return the plate, ifudesign, mode, # binning type, and iteration number. # # Args: # name (str): Name of the DAP file. # # Returns: # int, int, str, str, int : The plate, ifudesign, mode ('RSS' or # 'CUBE'), bin type, and iteration number of the DAP file, pulled # from the name of the file. # # Raises: # TypeError: Raised if *name* is not a string. # ValueError: Raised if if the file name does not look like a DRP # file because it does not include 'manga-', '-BIN', or # '.fits'. # """ # if (type(name) != str): # raise TypeError("File name must be a string.") # if (str.find(name, 'manga-') == -1 or str.find(name, '-LOG') == -1 # or str.find(name, 'BIN-') == -1 or str.find(name, '.fits') == -1): # raise ValueError("String does not look like a DAP fits file name.") # # plate_start = str.find(name, '-')+1 # plate_end = str.find(name,'-',plate_start) # plate = int(name[plate_start:plate_end]) # # ifudesign_end = str.find(name,'-',plate_end+1) # ifudesign = int(name[plate_end+1:ifudesign_end]) # # mode_start = str.find(name,'LOG')+3 # mode_end = str.find(name,'_BIN-') # mode = name[mode_start:mode_end] # # bintype_end = str.find(name,'-',mode_end+5) # bintype = name[mode_end+5:bintype_end] # # niter_end = str.find(name,'.fits',bintype_end) # niter = int(name[bintype_end+1:niter_end]) # # return plate, ifudesign, mode, bintype, niter
[docs]class DefaultConfig: """ A wrapper for the ConfigParser class that handles None values and provides some convenience functions. """ def __init__(self, f=None, interpolate=False): # TODO: May not need extended interpolation anymore... self.cnfg = ConfigParser(environ, allow_no_value=True, interpolation=ExtendedInterpolation()) \ if interpolate else ConfigParser(allow_no_value=True) if f is None: return self.read(f) def __getitem__(self, key): return self.cnfg['default'][key] def __iter__(self): return self.cnfg.options('default').__iter__()
[docs] def read(self, f): _f = Path(f).resolve() if not _f.exists(): raise FileNotFoundError(f'Configuration file not found: {_f}') self.cnfg.read(f)
[docs] def keyword_specified(self, key): return key in self and self[key] is not None
[docs] def all_required(self, keys): for k in keys: if k not in self or self[k] is None: return False return True
[docs] def get(self, key, default=None): return default if key not in self or self.cnfg['default'][key] is None \ else self.cnfg['default'][key]
[docs] def getint(self, key, default=None): return default if key not in self or self.cnfg['default'][key] is None \ else self.cnfg['default'].getint(key)
[docs] def getbool(self, key, default=None): return default if key not in self or self.cnfg['default'][key] is None \ else self.cnfg['default'].getboolean(key)
[docs] def getfloat(self, key, default=None): return default if key not in self or self.cnfg['default'][key] is None \ else self.cnfg['default'].getfloat(key)
[docs] def getlist(self, key, evaluate=False, default=None): if key not in self or self.cnfg['default'][key] is None: return default return [ eval(e.strip()) if evaluate else e.strip() \ for e in self.cnfg['default'][key].split(',') ]