Source code for irfpy.util.irfpyrc

''' Configure file support for irfpy library.

.. codeauthor:: Yoshifumi Futaana

.. versionchanged:: 4.4.0
   The priority order of the setup file in :class:`Rc` has been changed.

Configure files are used to make irfpy library portable.
For example, consider cases you want to access a local file,
for example, a data file.
The location of the data file may differ user by user, or even computer by computer.
Thus, the location is *not* recommended to be hard-coded into any library code.

Instead, a configuration file should be used.

See more detailed example in :ref:`recipe_irfpyrc`.

.. autosummary::

    Rc
'''
import os

import configparser
import warnings
import irfpy.util.exception as ex

import logging
_logger = logging.getLogger(__name__)


class _SingleRcFile:
    ''' Class treating individual configure file.

    Class treating individual configure file.
    From v1.8, the use of RcFile explicitly is **not** recommended, instead,
    :class:`Rc` class is recommended.
    :class:`Rc` handles multiple configure file at once.
    '''
    def __init__(self, name=None):
        ''' Open a configure file.

        :param name: Name of the RC file.  Absolute path.
        '''
        if name is None:
            name = os.path.join(os.getenv('HOME'), '.pyanarc')
            _logger.debug('Config file name = %s' % name)

        self.config = configparser.ConfigParser()
        self.name = os.path.abspath(name)

        if os.path.exists(name):
            _logger.info('Config file %s loaded.' % name)
            self.config.read(name)
        else:
            _logger.warning('Config file %s not found.  Using default values.' % name)

    def get(self, subproject, parameter):
        ''' Get a value of the specified parameter of particular subproject.
        '''
        try:
            val = self.config.get(subproject, parameter)
        except (configparser.NoOptionError, configparser.NoSectionError) as e:
            val = None
        return val

    def __str__(self):
        return self.name

    def __eq__(self, other):
        return self.name == other.name


_template = '\n'.join(['',
                       '=' * 80,
                       'irfpyrc: No entry ``{parameter}`` in ``{subsection}`` section is found.',
                       'Create a file .irfpyrc at the current directory or home directory with following entry:',
                       '-' * 80,
                       '[{subsection}]',
                       '{parameter} = {default_value}',
                       '-' * 80,
                       'See https://irfpy.irf.se/projects/util/cookbook/recipe_irfpyrc.html#recipe-irfpyrc',
                       'for configure file.',
                       '=' * 80,
                       ])
''' A template string for warning or exceptioin '''


[docs]class Rc: ''' Rc class represents the configuration files. Library developer may instance this class to use the configuration system for ``irfpy``. By default, the following files are loaded in the priority order. .. versionchanged:: 4.4.0 The priority order is changed. At the current directory: - ``./.irfpyrc`` -- Recommended for Mac/Linux users - ``./irfpy.ini`` -- Recommended for Windows/Mac/Linux users - ``./irfpy.rc`` - ``./.pyanarc`` -- Not recommended. Will be deprecated. At the home directory (for Mac/Linux): - ``${HOME}/.irfpyrc`` -- Recommended for Mac/Linux users - ``${HOME}/irfpy.ini`` -- Recommended for Mac/Linux users - ``${HOME}/irfpy.rc`` - ``${HOME}/.pyanarc`` -- Not recommended. Will be deprecated. At the home directory (for Windows): - ``%USERPROFILE%/.irfpyrc`` - ``%USERPROFILE%/irfpy.ini`` -- Recommended for Windows users - ``%USERPROFILE%/irfpy.rc`` - ``%USERPROFILE%/.pyanarc`` -- Not recommended. Will be deprecated. Here ``${HOME}`` represents the home folder for Mac/Linux (e.g. ``/home/username`` or ``/Users/username``) and ``%USERPROFILE%`` is the home directory for Windows (e.g. ``/Users/username``). The usual exercise of using the configure files are: >>> rc = Rc() >>> rc.get('subproject', 'option') If no entry is found for all the configure files, None is returned. Instead, ou can specify the ``default`` keyword; then the returned will be the default if there is no relevant entry. >>> prm = rc.get('subproject', 'option', default="https://irfpy.irf.se/") >>> print(prm) https://irfpy.irf.se/ If you want to add a file into the configure list, you can use :meth:`append` method. In this case, the specified file is added as the most prioritized configure file. >>> if os.path.exists('rcfile/pyanarc.special'): ... rc.append('rcfile/pyanarc.special') If you do not want to load the configure file, you may use :meth:`erase` method. This will clear the loaded configure files. >>> rc.erase() >>> print(len(rc)) 0 ''' def __init__(self, load_default=True, raises=False): """ :keyword raises: Raises exception if the entry is not found. :keyword load_default: Load default files. If *False*, no files are loaded. You may use :meth:`append` method to load manually. """ self.rclist = [] # Initialize default file names in a priority order. default_names = ['.irfpyrc', 'irfpy.ini', 'irfpy.rc', '.pyanarc' ] default_paths = ['./'] home = os.getenv('HOME') if home is not None: default_paths.append(home) home = os.getenv('USERPROFILE') if home is not None: default_paths.append(home) defaults = [] for dp in default_paths: for dn in default_names: defaults.append(os.path.join(dp, dn)) for rcfilename in defaults: if os.path.exists(rcfilename): rc = self.append(rcfilename, first=False) # Append the RC file in the end self._raises = raises
[docs] def append(self, filename, first=True): ''' Append the specified file name to the configure file list Append the specified file name to the configure file list. :param filename: Configure file name. :keyword first: If *True* (default), the configure file is added to the top of the list. If *False*, the configure file is added to the end of the list. Usually, user's configure file should be added to the top. :returns: A configure file object. :raises IOError: When the configure file is not found. ''' fname = os.path.expandvars(filename) if os.path.exists(fname): rc = _SingleRcFile(fname) # If _SingleRcFile object is in the list if rc in self.rclist: self.rclist.remove(rc) # Append the given file. if first: self.rclist.insert(0, rc) else: self.rclist.append(rc) # Append the files in "include" section of the given file. self._include(rc) else: raise IOError('!!!! File %s not found.' % fname)
def _include(self, rc): """ Append files in ``include`` entry to the configure file list. :param rc: RC file object. :type rc: :class:`_SingleRcFile` :return: None """ include_files = rc.get('DEFAULT', 'include') if include_files is None: return include_files = include_files.split() for include_file in include_files: include_file = include_file.strip() self.append(include_file, first=False)
[docs] def erase(self): ''' Erase the existing configure file list. ''' self.rclist = []
[docs] def get(self, subproject, parameter, error_sample="<relevant_value>", default=None): ''' Get the value from the configuration files. :param subproject: Subsection of RC file. :param parameter: Parameter in the subsection of RC file. :keyword error_sample: Sample string for displaying in the warning. :keyword default: The default return value, in case the ``subproject`` and ``parameter`` is not in the list. ''' for rc in self.rclist: val = rc.get(subproject, parameter) if val is not None: return val if default: return default if self._raises: raise NoEntryFoundError(subproject, parameter) else: _logger.warning(_template.format(subsection=subproject, parameter=parameter, default_value=error_sample)) return None
def __str__(self): s = '<Rc with %d RcFile instance(s):\n' % len(self.rclist) for f in self.rclist: s += " " + str(f) + "\n" s += ">\n" return s def __len__(self): return len(self.rclist)
[docs]class NoEntryFoundError(ex.IrfpyError): def __init__(self, subproject, parameter, error_sample='<relevant_value>'): warnings.warn(_template.format(subsection=subproject, parameter=parameter, default_value=error_sample)) self.value = "No entry ``{parameter}`` in the ``{subsection}`` section.".format(parameter=parameter, subsection=subproject)