1 """Fortran namelist interface. 3 The ``Namelist`` is a representation of a Fortran namelist and its contents in 6 :copyright: Copyright 2014 Marshall Ward, see AUTHORS for details. 7 :license: Apache License, Version 2.0, see LICENSE for details. 9 from __future__
import print_function
15 from StringIO
import StringIO
17 from io
import StringIO
19 from collections
import OrderedDict
21 from ordereddict
import OrderedDict
29 """Representation of Fortran namelist in a Python environment. 31 Namelists can be initialised as empty or with a pre-defined `dict` of 32 `items`. If an explicit default start index is required for `items`, then 33 it can be initialised with the `default_start_index` input argument. 35 In addition to the standard methods supported by `dict`, several additional 36 methods and properties are provided for working with Fortran namelists. 40 """Create the Namelist object.""" 44 if (args
and not isinstance(args[0], OrderedDict)
and 45 isinstance(args[0], dict)):
46 s_args[0] = sorted(args[0].items())
54 super(Namelist, self).
__init__(*s_args, **kwds)
61 if '_complex' in self:
62 for key
in self[
'_complex']:
63 if all(isinstance(v, list)
for v
in self[key]):
64 self[key] = [complex(*v)
for v
in self[key]]
66 self[key] = complex(*self[key])
82 if (platform.python_implementation() ==
'PyPy' and 83 platform.python_version_tuple()[0] ==
'2'):
84 for key, value
in self.items():
88 """Case-insensitive interface to OrderedDict.""" 92 """Case-insensitive interface to OrderedDict.""" 93 return super(Namelist, self).
__delitem__(key.lower())
96 """Case-insensitive interface to OrderedDict.""" 97 return super(Namelist, self).
__getitem__(key.lower())
100 """Case-insensitive interface to OrderedDict. 102 Python dict inputs to the Namelist, such as derived types, are also 103 converted into Namelists. 105 if isinstance(value, dict)
and not isinstance(value, Namelist):
110 for i, v
in enumerate(value):
119 super(Namelist, self).
__setitem__(key.lower(), value)
122 """Print the Fortran representation of the namelist. 124 Currently this can only be applied to the full contents of the namelist 125 file. Indiviual namelist groups or values may not render correctly. 128 if all(isinstance(v, Namelist)
for v
in self.values()):
131 print(repr(self), file=output)
133 nml_string = output.getvalue().rstrip()
141 """Set the maximum number of characters per line of the namelist file. 143 Tokens longer than ``column_width`` are allowed to extend past this 150 """Validate and set the column width.""" 151 if isinstance(width, int):
155 raise ValueError(
'Column width must be nonnegative.')
157 raise TypeError(
'Column width must be a nonnegative integer.')
161 r"""Set the whitespace indentation of namelist entries. 163 This can be set to an integer, denoting the number of spaces, or to an 164 explicit whitespace character, such as a tab (``\t``). 171 """Validate and set the indent width.""" 173 if isinstance(value, str):
177 raise ValueError(
'String indentation can only contain ' 181 elif isinstance(value, int):
185 raise ValueError(
'Indentation spacing must be nonnegative.')
188 raise TypeError(
'Indentation must be specified by string or space ' 193 """Append commas to the end of namelist variable entries. 195 Fortran will generally disregard any commas separating variable 196 assignments, and the default behaviour is to omit these commas from the 197 output. Enabling this flag will append commas at the end of the line 198 for each variable assignment. 204 """Validate and set the comma termination flag.""" 205 if not isinstance(value, bool):
206 raise TypeError(
'end_comma attribute must be a logical type.')
211 """Print group and variable names in uppercase.""" 216 """Validate and set the uppercase flag.""" 217 if not isinstance(value, bool):
218 raise TypeError(
'uppercase attribute must be a logical type.')
223 """Set the namelist floating point format. 225 The property sets the format string for floating point numbers, 226 following the format expected by the Python ``format()`` function. 232 """Validate and set the upper case flag.""" 233 if isinstance(value, str):
235 '{0:{1}}'.format(1.23, value)
239 raise TypeError(
'Floating point format code must be a string.')
244 """Set the string representation of logical values. 246 There are multiple valid representations of True and False values in 247 Fortran. This property sets the preferred representation in the 250 The properties ``true_repr`` and ``false_repr`` are also provided as 251 interfaces to the ``logical_repr`` tuple. 252 (Default: ``.false., .true.``) 258 """Set the string representation of logical values.""" 259 if not any(isinstance(value, t)
for t
in (list, tuple)):
260 raise TypeError(
"Logical representation must be a tuple with " 261 "a valid true and false value.")
262 if not len(value) == 2:
263 raise ValueError(
"List must contain two values.")
270 """Set the string representation of logical true values. 272 This is equivalent to the second element of ``logical_repr``. 277 def true_repr(self, value):
278 """Validate and set the logical true representation.""" 279 if isinstance(value, str):
280 if not (value.lower().startswith(
't')
or 281 value.lower().startswith(
'.t')):
282 raise ValueError(
"Logical true representation must start with " 287 raise TypeError(
'Logical true representation must be a string.')
290 def false_repr(self):
291 """Set the string representation of logical false values. 293 This is equivalent to the first element of ``logical_repr``. 298 def false_repr(self, value):
299 """Validate and set the logical false representation.""" 300 if isinstance(value, str):
301 if not (value.lower().startswith(
'f')
or 302 value.lower().startswith(
'.f')):
303 raise ValueError(
"Logical false representation must start " 308 raise TypeError(
'Logical false representation must be a string.')
311 def start_index(self):
312 """Set the starting index for each vector in the namelist. 314 ``start_index`` is stored as a dict which contains the starting index 315 for each vector saved in the namelist. For the namelist ``vec.nml`` 318 .. code-block:: fortran 327 the ``start_index`` contents are 332 >>> nml = f90nml.read('vec.nml') 333 >>> nml['vec_nml'].start_index 334 {'b': [0], 'c': [3], 'd': [None, None]} 336 The starting index of ``a`` is absent from ``start_index``, since its 337 starting index is unknown and its values cannot be assigned without 338 referring to the corresponding Fortran source. 343 def start_index(self, value):
344 """Validate and set the vector start index.""" 346 if not isinstance(value, dict):
347 raise TypeError(
'start_index attribute must be a dict.')
352 """Set the default start index for vectors with no explicit index. 354 When the `default_start_index` is set, all vectors without an explicit 355 start index are assumed to begin with `default_start_index`. This 356 index is shown when printing the namelist output. 359 If set to `None`, then no start index is assumed and is left as 360 implicit for any vectors undefined in `start_index`. 364 @default_start_index.setter
366 if not isinstance(value, int):
367 raise TypeError(
'default_start_index must be an integer.')
370 def write(self, nml_path, force=False, sort=False):
371 """Write Namelist to a Fortran 90 namelist file. 373 >>> nml = f90nml.read('input.nml') 374 >>> nml.write('out.nml') 376 nml_is_file = hasattr(nml_path,
'read')
377 if not force
and not nml_is_file
and os.path.isfile(nml_path):
378 raise IOError(
'File {0} already exists.'.format(nml_path))
380 nml_file = nml_path
if nml_is_file
else open(nml_path,
'w')
388 """Update the namelist from another partial or full namelist. 390 This is different from the intrinsic `update()` method, which replaces 391 a namelist section. Rather, it updates the values within a section. 393 for sec
in nml_patch:
396 self[sec].update(nml_patch[sec])
398 def _writestream(self, nml_file, sort=False):
399 """Output Namelist to a streamable file object.""" 404 sel =
Namelist(sorted(self.items(), key=
lambda t: t[0]))
408 for grp_name, grp_vars
in sel.items():
410 if isinstance(grp_vars, list):
411 for g_vars
in grp_vars:
416 def _write_nmlgrp(self, grp_name, grp_vars, nml_file, sort=False):
417 """Write namelist group to target file.""" 423 grp_name = grp_name.upper()
426 grp_vars =
Namelist(sorted(grp_vars.items(), key=
lambda t: t[0]))
428 print(
'&{0}'.format(grp_name), file=nml_file)
430 for v_name, v_val
in grp_vars.items():
432 v_start = grp_vars.start_index.get(v_name,
None)
434 for v_str
in self.
_var_strings(v_name, v_val, v_start=v_start):
435 nml_line = self.
indent +
'{0}'.format(v_str)
436 print(nml_line, file=nml_file)
438 print(
'/', file=nml_file)
440 def _var_strings(self, v_name, v_values, v_idx=None, v_start=None):
441 """Convert namelist variable to list of fixed-width strings.""" 443 v_name = v_name.upper()
452 i_s = v_start[::-1][len(v_idx)]
if v_start
else None 469 for idx, val
in enumerate(v_values, start=i_s):
470 v_idx_new = v_idx + [idx]
471 v_strs = self.
_var_strings(v_name, val, v_idx=v_idx_new,
473 var_strs.extend(v_strs)
476 elif isinstance(v_values, Namelist):
477 for f_name, f_vals
in v_values.items():
478 v_title =
'%'.join([v_name, f_name])
480 v_start_new = v_values.start_index.get(f_name,
None)
484 var_strs.extend(v_strs)
491 i_s = v_start[::-1][len(v_idx)]
if v_start
else 1
493 for idx, val
in enumerate(v_values, start=i_s):
499 v_title = v_name +
'({0})'.format(idx)
502 var_strs.extend(v_strs)
505 use_default_start_index =
False 506 if not isinstance(v_values, list):
507 v_values = [v_values]
508 use_default_start_index =
False 515 if v_idx
or v_start
or use_default_start_index:
518 if v_start
or use_default_start_index:
528 i_e = i_s + len(v_values) - 1
531 v_idx_repr +=
'{0}'.format(i_s)
533 v_idx_repr +=
'{0}:{1}'.format(i_s, i_e)
539 v_idx_repr +=
', '.join(str(i)
for i
in v_idx[::-1])
551 for v_val
in v_values:
553 v_header = v_name + v_idx_repr +
' = ' 556 column_width = len(self.
indent + v_header) + 1
560 v_width = column_width - len(self.
indent + v_header)
562 if len(val_line) < v_width:
563 val_line += self.
_f90repr(v_val) +
', ' 565 if len(val_line) >= v_width:
566 val_strs.append(val_line.rstrip())
571 val_strs.append(val_line.rstrip())
574 if self.
end_comma or v_values[-1]
is None:
577 val_strs[-1] = val_strs[-1][:-1]
581 var_strs.append(
'{0}{1} = {2}' 582 ''.format(v_name, v_idx_repr,
583 val_strs[0]).strip())
585 for v_str
in val_strs[1:]:
586 var_strs.append(
' ' * len(v_header) + v_str)
591 """Return a dict equivalent to the namelist. 593 Since Fortran variables and names cannot start with the ``_`` 594 character, any keys starting with this token denote metadata, such as 597 The ``complex_tuple`` flag is used to convert complex data into an 598 equivalent 2-tuple, with metadata stored to flag the variable as 599 complex. This is primarily used to facilitate the storage of the 600 namelist into an equivalent format which does not support complex 601 numbers, such as JSON or YAML. 604 nmldict = OrderedDict(self)
608 for key, value
in self.items():
609 if isinstance(value, Namelist):
610 nmldict[key] = value.todict(complex_tuple)
612 elif isinstance(value, complex)
and complex_tuple:
613 nmldict[key] = [value.real, value.imag]
615 nmldict[
'_complex'].append(key)
617 nmldict[
'_complex'] = [key]
619 elif isinstance(value, list):
621 for idx, entry
in enumerate(value):
622 if isinstance(entry, Namelist):
623 nmldict[key][idx] = entry.todict(complex_tuple)
625 elif isinstance(entry, complex)
and complex_tuple:
626 nmldict[key][idx] = [entry.real, entry.imag]
631 nmldict[
'_complex'].append(key)
633 nmldict[
'_complex'] = [key]
641 def _f90repr(self, value):
642 """Convert primitive Python types to equivalent Fortran strings.""" 643 if isinstance(value, bool):
645 elif isinstance(value, numbers.Integral):
647 elif isinstance(value, numbers.Real):
649 elif isinstance(value, numbers.Complex):
651 elif isinstance(value, basestring):
656 raise ValueError(
'Type {0} of {1} cannot be converted to a Fortran' 657 ' type.'.format(type(value), value))
659 def _f90bool(self, value):
660 """Return a Fortran 90 representation of a logical value.""" 663 def _f90int(self, value):
664 """Return a Fortran 90 representation of an integer.""" 667 def _f90float(self, value):
668 """Return a Fortran 90 representation of a floating point number.""" 671 def _f90complex(self, value):
672 """Return a Fortran 90 representation of a complex number.""" 673 return '({0:{fmt}}, {1:{fmt}})'.format(value.real, value.imag,
676 def _f90str(self, value):
677 """Return a Fortran 90 representation of a string.""" 679 result = repr(str(value)).replace(
"\\'",
"''").replace(
'\\"',
'""')
682 result = result.replace(
'\\\\',
'\\')
688 """Return True if list contains either values of type `vtype` or None.""" 689 return (isinstance(val, list)
and 690 any(isinstance(v, vtype)
for v
in val)
and 691 all((isinstance(v, vtype)
or v
is None)
for v
in val))
def todict(self, complex_tuple=False)
def __delitem__(self, key)
def _writestream(self, nml_file, sort=False)
def _write_nmlgrp(self, grp_name, grp_vars, nml_file, sort=False)
def default_start_index(self)
def __setitem__(self, key, value)
def write(self, nml_path, force=False, sort=False)
def __contains__(self, key)
def _f90repr(self, value)
def __init__(self, args, kwds)
def _var_strings(self, v_name, v_values, v_idx=None, v_start=None)
def _f90complex(self, value)
def _f90bool(self, value)
def _f90float(self, value)
def __getitem__(self, key)
def patch(self, nml_patch)
def is_nullable_list(val, vtype)