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