"""Handles FLASH's runtime parameter file domain-specific language. Since this language is
basically a key-value store, we translate such files to/from Python dictionaries. An AST
representation is not needed."""
import re
from itertools import izip_longest, groupby
# pre-compiled regexes
_par_line_pat = ("^\s*(\w+)\s*=\s*"
"""([^'"][^#\s]*|'{1,3}?[^'\n]*?'{1,3}?|"{1,3}?[^"\n]*?"{1,3}?)"""
)
_par_line_reg = re.compile(_par_line_pat, re.M)
_indexed_key_reg = re.compile("(\w+)_(\d+)$")
_par_int_reg = re.compile("^\d+$")
_par_str_reg = re.compile("""^(".*"|'.*')$""")
_par_bool_reg = re.compile("\.([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])\.")
_par_float_reg = re.compile("^[+-]?(\d+\.?|\.?\d+)\d*[Ee]?[+-]?\d*$")
_py_match_convert = [
(_par_str_reg, lambda v: v[1:-1]),
(_par_int_reg, int),
(_par_float_reg, float),
(_par_bool_reg, lambda v: '.true.' == v.lower()),
]
def _grouper(n, iterable, fillvalue=None):
"""From itertools; grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"""
args = [iter(iterable)] * n
return list(izip_longest(fillvalue=fillvalue, *args))
def _flat_reshape(iterable, shape):
vals = list(iterable)
ndims = len(shape)
nelems = 1
for i in range(ndims):
nelems *= shape[i]
assert nelems == len(vals)
for i in range(ndims - 1, 0, -1):
vals = _grouper(shape[i], vals)
return vals
def _nest_flat(iterable):
"""Takes a flat list whose elements are tuples
of (index, value) items and returns a nested list
of values, placed by the index."""
# Ensure dense at this level
i00 = set([i[0][0] for i in iterable])
min_index = min(i00)
max_index = max(i00)
if len(i00) != max_index - min_index + 1:
raise IndexError("iterable not dense at this level of nesting")
# return final state
if all([1 == len(i[0]) for i in iterable]):
return [i[1] for i in iterable]
# Nest by a single level
nested = [sorted(b for b in a[1]) for a in groupby(iterable, lambda x: x[0][0])]
# Remove an index level
reduced_nested = []
for nest in nested:
reduced = [(n[0][1:], n[1]) for n in nest]
reduced = _nest_flat(reduced)
reduced_nested.append(reduced)
return reduced_nested
def _isiterable(iterable):
try:
iter(iterable)
b = True
except TypeError:
b = False
return b
def _par_convert_iterable(key, value):
flattened = {}
for i, v in enumerate(value):
flatkey = key + "_{0}".format(i + 1)
for isfunc, extra_args, converter in _par_match_convert:
if isfunc(v, *extra_args):
flatvalues = converter(flatkey, v)
break
else:
msg = "parameter type could not be inferred: {0} = {1}".format(flatkey, v)
raise ValueError(msg)
flattened.update(flatvalues)
return flattened
_par_match_convert = [
(isinstance, [basestring], lambda k, v: {k: '"{0}"'.format(v)}),
(isinstance, [float], lambda k, v: {k: '{0:.8e}'.format(v)}),
(isinstance, [bool], lambda k, v: {k: '.true.' if v else '.false.'}), # Must be before int
(isinstance, [int], lambda k, v: {k: str(v)}),
(isinstance, [long], lambda k, v: {k: str(v)}),
(_isiterable, [], _par_convert_iterable),
]
[docs]def load(parfile):
"""Loads a FLASH runtime parameter file, such as flash.par,
and returns a Python dictionary."""
# get the file handler, if not provided
opened_here = False
if isinstance(parfile, basestring):
parfile = open(parfile)
opened_here = True
# read the file in.
rawfile = parfile.read()
if opened_here:
parfile.close()
# parse file into dictionary
rawdict = dict(_par_line_reg.findall(rawfile))
# transform to python-form
pydict = py_form(rawdict)
return pydict
[docs]def dump(pydict, parfile="flash.par", mode='w'):
"""Writes a FLASH runtime parameter file, such as flash.par,
from a Python dictionary. Such dictionaries mus be fairly
simple as the runtime parameters file specification is not
very sophisticatd."""
# convert dict to par-form
pardict = par_form(pydict)
# convert to string
paritems = pardict.items()
paritems.sort()
paritems = ["{0} = {1}".format(k, v) for k, v in paritems]
rawfile = "\n".join(paritems)
# get the file handler, if not provided
opened_here = False
if isinstance(parfile, basestring):
parfile = open(parfile, mode)
opened_here = True
# write the file
parfile.write(rawfile)
if opened_here:
parfile.close()