Source code for schrodinger.application.jaguar.user_config
"""
User profiles and configuration support for Jaguar input files
"""
# Contributors: Mark A. Watson
import contextlib
import io
import os
from distutils import file_util
from schrodinger.utils import fileutils
CONFIG_FILE = 'jaguar.config'
_macros = {}  # lazy instantiated macros dictionary
[docs]class JaguarConfigError(Exception):
    pass 
def _load_user_macros(fh):
    """
    Import the Jaguar configuration file.  The format is:
    <name1>
    key1=value1
    key2=value2
    ...
    <name2>
    keyA=valueA
    keyB=valueB
    where <name> is differentiated as any line NOT containing a "=" character.
    :type  fh: open file handle
    :param fh: file handle to an open, readable Jaguar configuration file
    """
    key = None
    macros = {}
    for line in fh:
        if line.strip():
            # Ignore blank lines
            if key and '=' in line:
                # Save macro body
                macros[key].append(line.rstrip())
            elif '=' not in line:
                # Initialize new macro
                key = line.strip()
                if key in macros:
                    msg = "Error: macro name %s is defined multiple times in " % key
                    msg += "configuration file: %s\n" % fh.name
                    msg += "Please delete all but one definition.\n"
                    raise JaguarConfigError(msg)
                macros[key] = []
    return macros
def _get_macro_dict():
    """
    Return the dictionary of macros.
    """
    config_file = get_config_filename()
    if os.path.isfile(config_file):
        with open(config_file, 'r') as fh:
            macros = _load_user_macros(fh)
    else:
        macros = {}
    return macros
def _write_preprocessed_infile(macros, infile, outfile):
    """
    Write expanded file <outfile> based on file <infile> and macro definitions.
    :type  infile: str
    :param infile: name of file to be preprocessed
    :type  outfile: str
    :param outfile: name of postprocessed file
    """
    # Write to memory buffer first in case infile == outfile
    with contextlib.closing(io.StringIO()) as buff:
        with open(infile, 'r') as ifh:
            for line in ifh:
                if line.strip() in macros:
                    for item in macros[line.strip()]:
                        buff.write(item + '\n')
                else:
                    buff.write(line)
        # Write memory buffer to outfile
        try:
            with open(outfile, 'w') as ofh:
                ofh.write(buff.getvalue())
        except IOError as e:
            msg = str(e)
            msg += '\n\nFailed to write to file %s\n' % outfile
            msg += 'Try adding write permissions to your input files.'
            raise JaguarConfigError(msg)
[docs]def get_config_filename():
    """
    Return path to Jaguar config file
    """
    return os.path.join(fileutils.get_directory_path(fileutils.APPDATA),
                        CONFIG_FILE) 
[docs]def preprocess_infile(infile, outfile):
    """
    Preprocess macros in file <infile> and write postprocessed file <outfile>.
    If files names are the same, <infile> is overwritten.
    :type  infile: str
    :param infile: name of file to be preprocessed
    :type  outfile: str
    :param outfile: name of postprocessed file
    """
    macros = _get_macro_dict()
    if infile != get_config_filename():
        if macros:
            # Create preprocessed input file
            _write_preprocessed_infile(macros, infile, outfile)
        elif infile != outfile:
            # Simply copy infile to outfile
            file_util.copy_file(infile, outfile, update=True) 
[docs]def get_macro(name, reread=False):
    """
    Return the dictionary of keywords set by the jaguar macro called name.
    If the macro does not exist, a ValueError is thrown.
    The function will lazily initialize the global _macros variable.
    :param name: name of macro to convert into a dictionary
    :type name: str
    :param reread: whether the configuration file should be re-read
    :type reread: bool
    :return: dictionary of jaguar keywords
    :rtype: dict
    :raise ValueError: if name is not in the dictionary of known macros
    :raise JaguarConfigError: if there are problems with the config file
    """
    global _macros
    if not _macros or reread:
        _macros = _get_macro_dict()
    try:
        macro = _macros[name]
    except:
        raise ValueError("Macro {} unknown.".format(name))
    # note a macro is only allowed to have key=value pairs in it (no macros)
    macro_dict = dict(
        (k.strip(), v.strip()) for k, v in (kv.split('=', 1) for kv in macro))
    return macro_dict