HWRF  trunk@4391
namelist.py
1 """!This module provides two different ways to generate Fortran
2 namelist files from HWRFConfig sections:
3 
4  NamelistInserter - given a file, or a multi-line string, finds
5  angle bracket (<vartype:stuff>) strings with values from an
6  HWRFConfig section. This is the suggested method for namelists
7  with very few configurable items.
8 
9  Conf2Namelist - given one or more Config sections, find values of
10  the form nlsec.nlkey=value. The nlsec specifies the namelist
11  &nlsec, the nlkey specifies the namelist variable name, and the
12  value is converted to a Python value and then a Fortran namelist
13  value. The Conf2Namelist provides ways to append multiple
14  Conf2Namelist objects' data to form Fortran one-dimensional
15  arrays. This is to support WRF namelists, where one specifies
16  each domain's data in a 1D array.
17 
18 In addition, this module provides two functions to_fortnml and
19 from_fortnml to convert between in-memory Python objects and strings
20 suitable for pasting in a Fortran namelist to achieve the same value
21 in Fortran."""
22 
23 import collections,re,fractions,datetime,ConfigParser, StringIO,logging
24 import hwrf.numerics
25 
26 from ConfigParser import NoOptionError,NoSectionError
27 from hwrf.exceptions import *
28 from hwrf.numerics import to_datetime, to_datetime_rel, to_fraction
29 
30 ##@var __all__
31 # Symbols exported by "from hwrf.namelist import *"
32 __all__=['to_fortnml','from_fortnml','Conf2Namelist','NamelistInserter']
33 
34 ##@var emptydict
35 # used for efficiency in a few places that need an empty dict()
36 emptydict=dict()
37 
38 class NamelistRecursion(Exception):
39  """!used to indicate namelist recursion
40 
41  This exception is used only for internal purposes and will never
42  be raised beyond this module."""
43 
44 def to_fortnml(py,exc_hint=''):
45  """!converts a Python object to fortran namelist format
46 
47  Converts the given python data structure to a string suitable for
48  representing in a Fortran namelist. It can handle bool, int,
49  float, string, datetime, fraction and a list (or tuple) of those.
50  A datetime is converted via strftime('"%Y-%m-%d_%H:%M:%S"'). The
51  optional exc_hint is used when raising exceptions to give an idea
52  of what file and line number, or config option name, generated the
53  exception.
54 
55  Conversions:
56  * True ==> "T"
57  * False ==> "F"
58  * int ==> str(py)
59  * float ==> str(py)
60  * fractions.Fraction ==> str(float(py))
61  * basestring ==> double-quoted string
62  * datetime.datetime ==> WPS/WRF format date stamp: %Y-%m-%D_%H:%M:%S
63  * list or tuple ==> comma-separated list of converted values
64 
65  Anything else will raise an exception.
66 
67  @return the converted value as a string
68  @param py the object to convert
69  @param exc_hint prepended to exception messages when exceptions are
70  raised. Should contain file and line information."""
71  try:
72  return __to_fortnml_impl(py,exc_hint=exc_hint)
73  except NamelistRecursion:
74  s=repr(py)
75  s=s[0:37]+'...' if len(s)>40 else s
76  raise NamelistValueError('%s: cannot convert nested python containers to Fortran namelist values.'%(exc_hint+s,))
77 
78 def __to_fortnml_impl(py,recursed=False,exc_hint=''):
79  """!internal function used to convert python objects to namelist syntax
80 
81  This function does the actual work of converting a Python object to
82  Fortran namelist syntax. Do not call it directly.
83  @param py the object to convert
84  @param recursed have we already recursed? Used to detect nested lists
85  @param exc_hint the exc_hint argument to to_fortnml()"""
86  if isinstance(py,bool): return 'T' if py else 'F'
87  if isinstance(py,int): return str(py)
88  if isinstance(py,float): return str(py)
89  if isinstance(py,fractions.Fraction): return str(float(py))
90  if isinstance(py,basestring): return '"'+re.sub('"','""',str(py))+'"'
91  if isinstance(py,datetime.datetime): return py.strftime('"%Y-%m-%d_%H:%M:%S"') # WPS format
92  if isinstance(py,list) or isinstance(py,tuple):
93  if recursed:
94  raise NamelistRecursion()
95  else:
96  return ', '.join([__to_fortnml_impl(x,recursed=True,
97  exc_hint=exc_hint) for x in py])
98  raise NamelistValueError('%s%s Unable to convert to a fortran namelist '
99  'value'%(exc_hint,repr(py)))
100 
101 ##@var fortnml_parse
102 # a regular expression for tokenizing a fortran namelist scalar value
103 fortnml_parse=re.compile("""(?ix)\s*(?:
104  (?P<fraction>[0-9]+[+][0-9]+[/][0-9]+) |
105  \"(?P<dqchar>(?:[^\"]+|\"\")+)\" |
106  \'(?P<sqchar>(?:[^\']+|\'\')+)\' |
107  (?P<real>
108  (?:(?:[+-]?[0-9]+\.[0-9]*|[+-]?\.[0-9]+)(?:[eE][+-]?[0-9]+)?) |
109  (?:[+-]?[0-9]+[eE][+-]?[0-9]+)
110  ) |
111  (?P<int>[+-]?[0-9]+) |
112  (?P<identifier>[a-zA-Z_][a-zA-Z0-9_]+|[a-eg-su-zA-EG-SU-Z_]) |
113  (?P<true>t|\.true\.) |
114  (?P<false>f|\.false\.) |
115  (?P<comment>[!#].*$) |
116  (?P<comma>\s*,\s*) |
117  (?P<bad>.)
118 )""")
119 """A regular expression for tokenizing a fortran namelist scalar value."""
120 
121 def from_fortnml(py):
122  """!Converts a fortran namelist value to a Python object
123 
124  This is the inverse function for to_fortnml. Given a string from
125  a Fortran namelist value, returns an equivalent python object.
126  Will throw NamelistValueError if an unrecognized object is
127  present, or NamelistRecursion if you send a nested container (such
128  as a list of lists).
129  @param py the string to convert
130  @return the Python object"""
131  out=[]
132  islist=False
133  for match in fortnml_parse.finditer(py):
134  tok=match.lastgroup
135  if tok is None or tok=='bad':
136  raise NamelistValueError('%s: cannot parse namelist value'%(py,))
137  elif tok=='comment': break
138  elif tok=='int': out.append(int(match.group(tok)))
139  elif tok=='real': out.append(float(match.group(tok)))
140  elif tok=='true': out.append(True)
141  elif tok=='false': out.append(False)
142  elif tok=='fraction': out.append(to_fraction(match.group(tok)))
143  elif tok=='dqchar': out.append(re.sub('""','"',match.group(tok)))
144  elif tok=='sqchar': out.append(re.sub("''","'",match.group(tok)))
145  elif tok=='comma': islist=True
146  elif tok=='identifier': out.append(match.group(tok))
147  if len(out)==1:
148  return out[0]
149  elif len(out)==0:
150  raise NamelistValueError('%s: does not specify any data',(py,))
151  else:
152  return out
153 
154 class NamelistInserter(object):
155  """!Insert config file data into a Fortran namelist file.
156 
157  This class parses an input file that contains a partial namelist,
158  inserting missing values of the format <s:cycle> with values from
159  a ConfigParser-like object. The <s:cycle> sorts of text follow a
160  specific format:
161 
162  * @<s:varname@> -- insert a string surrounded by double quotes taken
163  from the specified conf variable
164  * @<f:varname@> -- insert a float, taken from conf variable varname
165  * @<r:varname@> -- same as <f:varname>
166  * @<i:varname@> -- insert an integer, taken from conf variable varname
167  * @<l:varname@> -- insert a logical, taken from conf variable varname
168  * @<b:varname@> -- same as <l:varname>
169 
170  @<d:varname@> -- the conf variable is converted to a
171  datetime.datetime using hwrf.numerics.to_datetime, and then to a
172  string of the format "YYYY-MM-DD_HH:MM:SS". If atime is
173  specified to the parse subroutine, then the value is allowed to
174  be a difference relative to the atime (as accepted by
175  hwrf.numerics.to_datetime_rel).
176 
177  @<u:varname@> -- convert the conf variable to a string, and dump its
178  value unquoted. This is used, for example, to have the name of a
179  namelist variable be generated from a conf file.
180 
181  You can also specify angle braces without a type:
182 
183  @<varname@>
184 
185  to ask for the variable to be converted by guessing the type that
186  was intended in the conf file. For example:
187 
188  This conf file:
189  @code
190  [conf]
191  var=T,F,T
192  @endcode
193 
194  With this namelist:
195 
196  @code
197  &nl
198  myvar=@<var@> /
199  @endcode
200 
201  will produce:
202 
203  @code
204  &nl
205  myvar=.true., .false., .true. /
206  @endcode
207 
208  As for variables, one can request a subitem of a variable:
209 
210  * varname -- get variable varname
211  * vit[stormname] -- get variable "vit" and then get
212  vit.__getitem__["stormname"]
213 
214  for subscriptable types. This is mainly intended for vitals."""
215 
216  ## @var find_ltgt
217  # regular expression that finds namelist insertion data @<...@>
218  find_ltgt=re.compile('''(?P<pre>(?:[^<]|"(?:[^"]|""")*"|'(?:[^']|'{3})*')*)<(?:(?P<typ>[^:]*):)?(?P<var>[^>\[]*)(?:\[(?P<sub>[^\]]*)\])?>(?P<rest>.*)''')
219 
220  ## @var nlfalse
221  # regular expression that matches false values
222  nlfalse=re.compile('\A(?i)(?:f.*|.false.|n|no|0*[1-9][0-9]*)\Z')
223 
224  ## @var nltrue
225  # regular expression that matches true values
226  nltrue=re.compile('\A(?i)(?:t.*|.true.|y|yes|0)\Z')
227 
228  ## @var comment
229  # regular expression that matches comments
230  comment=re.compile('(?P<code>(?:[^!]|\"(?:[^\"]|\"\"\")*\"|\'(?:[^\']|\'\'\')*\')*)(?P<comment>!.*)?')
231 
232  def __init__(self,conf,section):
233  """!NamelistInserter constructor
234 
235  Creates a new NamelistInserter that will get its data from the
236  specified section of the HWRFConfig conf.
237  @param conf the hwrf.config.HWRFConfig object to use
238  @param section the section to read"""
239  self._conf=conf
240  self._section=section
241 
242  ##@var _conf
243  # the hwrf.config.HWRFConfig object sent to __init__(conf,section)
244 
245  ##@var _section
246  # the section to read, sent to __init__(conf,section)
247 
248  def parse(self,line_iterable,logger=None,source='<internal>',
249  raise_all=True,atime=None,ftime=None,**kwargs):
250  """!Generates the namelist, returning it as a string.
251 
252  Reads the config section and file, generating the namelist and
253  returning it as a string.
254 
255  @param line_iterable an iterator that iterates over each line of
256  the file.
257  @param logger optional: a logging.Logger to log messages, or None
258  to disable. Default: None
259  @param source for warning or error messages: the filename.
260  Default: "@<internal@>"
261  @param raise_all set to False to log messages instead of
262  raising exceptions. Raise only one exception at the end.
263  Default: True; raise an exception as soon as the first
264  error is seen.
265  @param atime Optional: the analysis time for conf.timestrinterp
266  @param ftime Optional: the forecast time for conf.timestrinterp
267  @param kwargs additional variables and their values for config
268  file accesses."""
269  assert(not isinstance(line_iterable,basestring))
270  if atime is not None:
271  atime=to_datetime(atime)
272  if ftime is not None:
273  ftime=to_datetime_rel(ftime,atime)
274  elif atime is not None:
275  ftime=atime
276  out=StringIO.StringIO()
277  conf=self._conf
278  section=self._section
279  iline=0
280  def synerr(what):
281  if logger is not None:
282  logger.warning('%s:%d: syntax error: %s'%(source,iline,what))
283  for line in line_iterable:
284  line=line.rstrip()
285  iline+=1
286  linepart=line
287  m=NamelistInserter.comment.match(linepart)
288  comment=''
289  if m:
290  code=m.group('code')
291  comment=m.group('comment')
292  if not code:
293  out.write(line)
294  continue
295  linepart=code
296  while len(linepart)>0:
297  m=NamelistInserter.find_ltgt.match(linepart)
298  if m:
299  pre=m.group('pre')
300  typ=m.group('typ')
301  var=m.group('var')
302  sub=m.group('sub')
303  rest=m.group('rest')
304  if rest:
305  assert(linepart!=rest)
306  linepart=rest
307  else:
308  linepart=''
309  if pre: out.write(pre)
310  if typ is None: typ='*'
311  if not typ: synerr('no output type specified')
312  elif not var: synerr('no variable specified')
313  elif len(typ)!=1:
314  synerr('output type must be one of: bdfilsu, not %s'
315  %(typ,))
316  elif typ not in 'bdfilrsuBDFILRSU*':
317  synerr('invalid type %s specified: only bdfilsu are '
318  'allowed'%(typ,))
319  else:
320  try:
321  if var in kwargs:
322  val=kwargs[var]
323  elif atime is not None:
324  val=conf.timestrinterp(
325  section,'{'+var+'}',ftime=ftime,
326  atime=atime,**kwargs)
327  else:
328  val=conf.strinterp(
329  section,'{'+var+'}',**kwargs)
330  except(KeyError,TypeError,ValueError,NoOptionError,
331  NoSectionError) as e:
332  if logger is not None:
333  logger.warning(
334  '%s:%d: cannot find variable %s'
335  %(source,iline,var))
336  if raise_all: raise
337  continue
338  if sub:
339  try:
340  newval=val[sub]
341  val=newval
342  except (TypeError,KeyError,ValueError,HWRFError) \
343  as e:
344  if logger is not None:
345  logger.warning('%s:%d: %s[%s]: %s'%(
346  source,iline,var,sub,str(e)),
347  exc_info=True)
348  if raise_all: raise
349  continue
350  try:
351  if typ in 'rRfF': typval=float(val)
352  elif typ in 'iI': typval=int(val)
353  elif typ in 'bBlL':
354  if isinstance(val,bool):
355  typval=val
356  elif isinstance(val,basestring):
357  if NamelistInserter.nlfalse.match(val):
358  typval=False
359  elif NamelistInserter.nltrue.match(val):
360  typval=True
361  else:
362  raise ValueError(
363  '%s is not a valid logical'
364  %(repr(val),))
365  else:
366  typval=bool(val)
367  elif typ in 'dD':
368  dval=from_fortnml(val)
369  if atime is not None:
370  typval=to_datetime_rel(dval,atime)
371  else:
372  typval=to_datetime(dval)
373  elif typ=='*':
374  typval=from_fortnml(val)
375  else: # types u and s
376  typval=str(val)
377  except (TypeError,KeyError,ValueError) as e:
378  if logger is not None:
379  logger.warning(
380  '%s:%d: cannot convert %s to type=%s: %s'
381  %(source,iline,repr(val),typ,str(e)),
382  exc_info=True)
383  if raise_all: raise
384  continue
385  if sub: fromthis='%s[%s]'%(var,sub)
386  else: fromthis=var
387  try:
388  if typ in 'bdfilrsBDFILRS*':
389  writeme=to_fortnml(
390  typval,exc_hint=fromthis+':')
391  else: # type u
392  writeme=str(typval)
393  except (TypeError,KeyError,ValueError) as e:
394  if logger is not None:
395  logger.warning(
396  '%s:%d: <%s:%s>=%s: error converting to '
397  'string: %s'
398  %(source,iline,typ,fromthis,repr(typval),
399  str(e)),exc_info=True)
400  if raise_all: raise
401  continue
402  out.write(writeme)
403  else:
404  # No more <typ:val> on this line. Write the rest.
405  out.write(linepart)
406  linepart=''
407  out.write(comment)
408  out.write('\n')
409  return out.getvalue()
410 
411 class Conf2Namelist(object):
412  """!Generates a Fortran namelist entirely from config files
413 
414  This class generates namelist information from any
415  ConfigParser-like object, starting at a specified conf section.
416  This differs from NamelistInserter in that no external file is
417  read - only the ConfigParser is used. Also, an
418  hwrf.config.HWRFConfig is not required; any ConfigParser-like
419  object is sufficient.
420 
421  When parsing the section, variables of the form nlsection.nlkey
422  will be put in namelist section nlsection, with variable name
423  nlkey. Variables that contain no "." but are valid Fortran
424  variable names (except for the variable "namelist") are called
425  "traits" and are accessible via trait_get, trait_set, and the
426  other trait_* subroutines. The special variable "namelist"
427  specifies a list of conf sections to recurse into. Variables in
428  the local conf section will always override variables in included
429  conf sections, and variables in later included conf sections will
430  override variables in earlier included conf sections.
431 
432  For example:
433  @code
434  conf=RawConfigParser()
435  conf.readfp(StringIO('''
436  [sec1]
437  happiness_quotient=0.7
438  physics.mp_physics=85
439  physics.cu_physics=84
440  namelist=sec2,sec3
441  [sec2]
442  physics.cu_physics=4
443  physics.bl_pbl_physics=93
444  [sec3]
445  physics.bl_pbl_physics=3
446  ''')
447  str(Conf2Namelist(conf,'sec1'))
448  @endcode
449  Will result in:
450  @code{.unparsed}
451  &physics
452  bl_pbl_physics=3
453  cu_physics=84
454  mp_physics=85
455  /
456  @endcode
457  and a trait accessible via self.trait_get('happiness_quotient')
458  """
459 
460  ##@var testnl
461  # a configuration string for testing
462  testnl='''[sec1]
463 happiness_quotient=0.7
464 physics.mp_physics=85
465 physics.cu_physics=84
466 namelist=sec2,sec3
467 [sec2]
468 physics.cu_physics=4
469 physics.bl_pbl_physics=93
470 domains.something=32
471 [sec3]
472 physics.bl_pbl_physics=3'''
473  """A test namelist for debugging"""
474 
475  ##@var nlentry
476  # A regular expression from re.compile. This is used to scan
477  # config section option names to detect if they are namelist
478  # variable names (format: section.variable).
479  nlentry=re.compile('\A(?:(?P<section>[a-zA-Z_][a-zA-Z0-9_]*)\.)?(?P<var>[a-zA-Z_][a-zA-Z0-9_%]*)\Z')
480 
481  ##@var nlfalse
482  # detects false Fortran logical constants
483  nlfalse=re.compile('\A(?i)(?:f.*|.false.)\Z')
484  """A regular expression from re.compile, to detect false Fortran logical values."""
485 
486  ##@var nltrue
487  # detects true Fortran logical constants
488  nltrue=re.compile('\A(?i)(?:t.*|.true.)\Z')
489  """A regular expression from re.compile, to detect true Fortran logical values."""
490 
491  ##@var TRAIT
492  # special section name for traits
493  TRAIT='-trait-'
494  """A special, fake, invalid namelist name for traits."""
495 
496  def copy(self,other):
497  """!Returns a copy of self.
498 
499  Creates a shallow copy of self
500  @param other unused; may be removed in the future"""
501  return Conf2Namelist(section_sorter=self.section_sorter,
502  var_sorters=self.var_sorters,
503  logger=self.logger,
504  nl=self.nl)
505 
506  ##@var section_sorter
507  # The cmp function used to sort sections
508 
509  ##@var var_sorters
510  # A dict mapping from section name to a cmp function used
511  # to sort namelist variables in that section
512 
513  ##@var nl
514  # A dict of dicts used to store namelist information
515 
516  def __init__(self,conf=None,section=None,section_sorter=None,
517  var_sorters=None,logger=None,nl=None,morevars=None):
518  """!Conf2Namelist constructor
519 
520  Creates a Conf2Namelist.
521 
522  @param conf the HWRFConfig object
523  @param section the section to start searching from.
524  @param section_sorter the cmp-like function to use to sort the
525  sections when generating the output namelist
526  @param var_sorters a dict-like mapping from section name to a
527  cmp-like function to use to sort variable names within
528  each section.
529  @param logger a logging.Logger object to use to log messages
530  @param nl a dict of dicts (or object that acts like that)
531  to use to initialize the Conf2Namelist. Warning: this
532  functionality is untested
533  @param morevars a dict with additional variables to use when
534  expanding strings This is simply passed to conf.items.
535  See the HWRFConfig documentation for details."""
536  if morevars is None: morevars=emptydict
537  self.section_sorter=None
538  self.var_sorters=var_sorters
539  if self.section_sorter is None:
540  self.section_sorter=cmp
541  self.nl=collections.defaultdict(lambda: collections.defaultdict())
542  if nl is not None:
543  for (sec,con) in nl.iteritems():
544  for key,val in con.iteritems():
545  nl[sec][key]=val
546  if conf is None or section is None:
547  return
548  initial=str(section)
549  touched=set()
550  parseme=[initial]
551  nlentry=Conf2Namelist.nlentry
552  while len(parseme)>0:
553  sec=parseme.pop()
554  if sec in touched:
555  continue
556  touched.add(sec)
557  if logger is not None:
558  logger.debug('Conf2Namelist now parsing section %s'%(sec,))
559  for key,value in conf.items(sec,morevars=morevars):
560  m=nlentry.match(key)
561  if m and m.group('section') is not None:
562  self.nl_set_if_unset(m.group('section'),m.group('var'),
563  from_fortnml(value))
564  elif key=='namelist':
565  # namelist=list,of,conf,sections
566  # Add each one to the list of sections to parse
567  for sec2 in value.split(','):
568  trim=sec2.strip()
569  if len(trim)>0 and not trim in touched:
570  parseme.append(trim)
571  elif re.match('\A[a-zA-Z_][a-zA-Z_0-9%]*\Z',key):
572  self.trait_set_if_unset(m.group('var'),
573  from_fortnml(value))
574  elif logger is not None:
575  logger.debug(
576  'Conf2Namelist ignoring %s = %s in section %s'
577  %(key,value,sec))
578  def nl_section(self,*args):
579  """!create namelists if they do not exist
580 
581  Ensures that the specified namelist exists. Returns self.
582  @param args list of namelist names"""
583  for section in args:
584  self.nl[str(section).lower()]
585  return self
586  def nl_set(self,section,var,data):
587  """!Sets a variable in a namelist
588 
589  Sets the value of a namelist's variable.
590  @param section the namelist name
591  @param var the name of the variable in the namelist
592  @param data the value. This can be a string,
593  datetime.datetime, int, float, fractions.Fraction or a list or
594  tuple of such"""
595  if not ( isinstance(data,basestring) or
596  isinstance(data,datetime.datetime) or
597  isinstance(data,int) or isinstance(data,float) or
598  isinstance(data,list) or
599  isinstance(data,fractions.Fraction) ):
600  raise TypeError('%s: invalid type for namelist (value=%s)'
601  %(data.__class__.__name__,repr(data)))
602  self.nl[str(section).lower()][str(var).lower()]=data
603  def nl_del(self,section,var):
604  """!Removes a variable from a namelist.
605 
606  Removes a variable from a namelist
607  @param section the namelist
608  @param var the variable to delete"""
609  try:
610  del self.nl[str(section).lower()][var]
611  except KeyError: pass
612  # Multistorm - jtf
613  def nl_del_sect(self,section):
614  """Removes a namelist section from the namelist"""
615  try:
616  del self.nl[str(section).lower()]
617  except KeyError: pass
618  def nl_have(self,section,var):
619  """!does this namelist have this variable?
620 
621  Determines if the namelist exists and has the variable.
622 
623  @return True if the namelist exists and it has the variable, or
624  False otherwise
625  @param section the string name of the namelist
626  @param var the string name of the variable"""
627  if section not in self.nl: return False
628  return var in self.nl[section]
629  # Multistorm - jtf
630  def nl_have_sect(self,section):
631  """Returns True if the namelist section exists in the namelist
632  and False otherwise"""
633  if section in self.nl: return True
634  return False
635  def nl_get(self,section,var,default=None):
636  """!get the value of a variable from a namelist
637 
638  Gets the value of a variable in a namelist. If the
639  default is supplied and non-None, it will be returned if the
640  variable does not exist. Raises NamelistKeyError if the
641  variable does not exist a the default is not provided.
642  @param section the namelist name
643  @param var the name of the variable in the namelist
644  @param default the value to return if the namelist or variable
645  do not exist """
646  s=str(section).lower()
647  v=str(var).lower()
648  try:
649  return self.nl[s][v]
650  except KeyError:
651  if default is not None:
652  return default
653  raise NamelistKeyError('no value given',s,v)
654  def nl_set_if_unset(self,section,var,data):
655  """!Sets the value of a namelist variable if it has no value.
656 
657  If the namelist variable has a value, this function does
658  nothing. Otherwise, it is the same as calling nl_set.
659  @param section the namelist name
660  @param var the variable name
661  @param data the value to set, passed to nl_set()"""
662  try:
663  self.nl_get(section,var)
664  except KeyError:
665  self.nl_set(section,var,data)
666  def nl_each(self,section):
667  """!Iterates over variable,value tuples in the given
668  namelist.
669  @param section the namelist over which to iterate"""
670  if not isinstance(section,basestring): section=str(section)
671  if not section in self.nl: return
672  for var,value in self.nl[section].iteritems():
673  yield var,value
674  def trait_each(self):
675  """!Iterates over variable,value tuples in the traits."""
676  assert(Conf2Namelist.TRAIT in self.nl)
677  assert(self.nl[Conf2Namelist.TRAIT])
678  for var,value in self.nl[Conf2Namelist.TRAIT].iteritems():
679  yield var,value
680  def trait_set(self,var,value):
681  """!Sets a trait's value.
682  This is the same as nl_set() but sets a trait. It simply
683  passes the special constant Conf2Namelist.TRAIT as the
684  namelist
685  @param var the name of the trait to set
686  @param value the value to set"""
687  return self.nl_set(Conf2Namelist.TRAIT,var,value)
688  def trait_del(self,var):
689  """!Deletes a trait.
690 
691  Deletes a trait. This is the same as calling nl_del passing
692  the Conf2Namelist.TRAIT constant as the section.
693  @param var the variable to delete."""
694  return self.nl_del(Conf2Namelist.TRAIT,var)
695  def trait_get(self,var,default=None):
696  """!Returns a trait's value.
697 
698  Returns the value of a trait. If a default is given and
699  non-None, returns the default if the trait does not exist.
700  This is the same as calling nl_get() passing the
701  Conf2Namelist.TRAIT as the section.
702  @param var the trait to get
703  @param default the default value if the trait is unset"""
704  return self.nl_get(Conf2Namelist.TRAIT,var,default)
705  def trait_have(self,var):
706  """!Returns True if the trait exists, and False otherwise.
707 
708  Determines if a trait is set. This is the same as passing
709  Conf2Namelist.TRAIT as the section argument to nl_have()
710  @param var the trait to query"""
711  return self.nl_have(Conf2Namelist.TRAIT,var)
712  def trait_set_if_unset(self,var,value):
713  """!Sets the traits value if it does not have one.
714 
715  Sets the value of a trait if the trait does not already have a
716  value. This is the same as calling nl_set_if_unset() passing
717  the special value Conf2Namelist.TRAIT as the section."""
718  return self.nl_set_if_unset(Conf2Namelist.TRAIT,var,value)
719  def join(self,others):
720  """!create array values by joining multiple Conf2Namelist objects
721 
722  Generates a new Conf2Namelist by looping over all arguments in
723  this namelist, and appending the other namelists' values in a
724  list. If the other namelist does not have a given value, then
725  this namelist's value, or the last namelist that had a value
726  for that argument, will be appended. Variables or namelist
727  sections that exist in the other namelists, but not this one,
728  will be ignored.
729 
730  @param others an iterable object (list, tuple) of Conf2Namelist"""
731  if len(others)==0:
732  return self.copy()
733  out=Conf2Namelist(None,None)
734  for secname,mydict in self.nl.iteritems():
735  outsec=out.nl[secname]
736  for var,value in mydict.iteritems():
737  thelist=[value]
738  default=value
739  for other in others:
740  nextval=other.nl_get(secname,var,default)
741  default=nextval
742  if isinstance(nextval,tuple) or isinstance(nextval,list):
743  thelist.extend(nextval)
744  else:
745  thelist.append(nextval)
746  outsec[var]=thelist
747  return out
748  def copy(self,section_subset=None,var_subset=None,other=None):
749  """!duplicates this object
750 
751  Returns a copy of this object, or if other is specified,
752  copies this object's contents into the target Conf2Namelist.
753  When copying into a target Conf2Namelist, only values that are
754  not already in that namelist will be copied. The copy has its
755  own data structures, so modifying the copy will not modify the
756  original. Optionally, you can copy only a subset of this
757  object:
758 
759  @param section_subset = a callable object that returns True
760  for each section to be kept
761  @param var_subset = a callable object that returns
762  True for each variable to be kept.
763 
764  @param other if specified, this must be a Conf2Namelist.
765  Instead of creating a new Conf2Namelist, data will be inserted
766  into this one."""
767  if other is None:
768  other=Conf2Namelist(None,None)
769  for s,sd in self.nl.iteritems():
770  if section_subset is None or section_subset(s):
771  outdict=other.nl[s]
772  for var,value in sd.iteritems():
773  if var_subset is None or var_subset(s,var):
774  try:
775  junk=outdict[var]
776  except KeyError:
777  outdict[var]=value
778  return other
779  def remove_traits(self):
780  """!Removes all traits.
781  Deletes the special Conf2Namelist.TRAIT namelist
782  @return self"""
783  if Conf2Namelist.TRAIT in self.nl:
784  del(self.nl[Conf2Namelist.TRAIT])
785  return self
786  def __str__(self):
787  """!synonym for make_namelist()
788 
789  Generates a Fortran namelist as a string. Equivalent to
790  make_namelist()."""
791  return self.make_namelist()
792  def make_namelist(self,section_sorter=None,var_sorters=None,
793  morevars=None):
794  """!generates the namelist as a string
795 
796  Returns the stringified namelist for this object, suitable for
797  writing to a file for Fortran to read. Optionally, you can
798  provide a sorting for the namelist entries:
799 
800  @param section_sorter = a cmp-like function for sorting sections by
801  name If absent, cmp is used.
802  @param var_sorters = a dict-like mapping of section names to cmp-like
803  objects for sorting variables within each section. If
804  absent, self.namelist_sorter(sectionname) is used
805  @param morevars a dict of additional variables which override
806  values set in self.nl"""
807  out=''
808  if section_sorter is None:
809  section_sorter=self.section_sorter
810 
811  sd=None
812  def getvar(var):
813  if morevars is not None and var in morevars:
814  return morevars[var]
815  else:
816  return sd[var]
817 
818  for sec in sorted(self.nl.iterkeys(),section_sorter):
819  sd=self.nl[sec]
820  if sec==Conf2Namelist.TRAIT:
821  out+='! Traits:\n'
822  else:
823  out+='&%s\n'%(sec,)
824  if sd:
825  sorter=None
826  if var_sorters is not None and sec in var_sorters:
827  sorter=var_sorters[sec]
828  if sorter is None:
829  sorter=self.namelist_sorter(sec)
830  if sec==Conf2Namelist.TRAIT:
831  out+="\n".join('! %s = %s,'%(var,to_fortnml(getvar(var),
832  exc_hint='%s%%%s='%(str(sec),str(var)))) \
833  for var in sorted(sd.keys(),sorter))
834  else:
835  out+="\n".join(' %s = %s,'%(var,to_fortnml(getvar(var),
836  exc_hint='%s%%%s='%(str(sec),str(var)))) \
837  for var in sorted(sd.keys(),sorter))
838  if sec==Conf2Namelist.TRAIT:
839  out+='\n\n'
840  else:
841  out+="\n/\n\n"
842  return out
843  def namelist_sorter(self,section):
844  """!return a sorting function for the variables in a namelist
845 
846  Returns a cmp function that orders namelist entries for the
847  specified namelist section. See the argument "cmp" of the
848  python built-in function sorted() for details.
849 
850  @param section the namelist or Conf2Namelist.TRAIT
851  @return a cmp-like function sorter(a,b) """
852  if self.var_sorters is not None:
853  if section in self.var_sorters:
854  return self.var_sorters[section]
855  return lambda x,y: cmp(x,y)
856  def set_sorters(self,section_sorter,var_sorters):
857  """!sets the sorting algorithms for namelists and variables
858 
859  Sets the cmp-like functions for sorting sections and
860  variables in each section. The section_sorter sorts sections.
861  The var_sorters is a dict-like mapping from section name to a
862  cmp-like function for sorting that section. If any sorter is
863  unspecified, cmp will be used. See the "cmp" argument of the
864  python built-in function sorted() for details.
865 
866  @param section_sorter a cmp-like function for sorting
867  namelist by namelist name. If section_sorters is None,
868  cmp is used.
869  @param var_sorters a dict-like mapping from namelist name to a
870  variable sorter function. Each variable sorter function must
871  be a cmp-like function that compares variable names. If
872  var_sorters is None, then cmp() will be used for all namelists.
873  @return self"""
874  self.section_sorter=cmp if section_sorter is None else section_sorter
875  self.var_sorters=var_sorters
876  return self
def trait_set_if_unset(self, var, value)
Sets the traits value if it does not have one.
Definition: namelist.py:712
Generates a Fortran namelist entirely from config files.
Definition: namelist.py:411
def trait_del(self, var)
Deletes a trait.
Definition: namelist.py:688
def trait_get
Returns a trait's value.
Definition: namelist.py:695
def to_fortnml
converts a Python object to fortran namelist format
Definition: namelist.py:44
Raised when hwrf.namelist cannot convert a value to or from Fortran namelist format.
Definition: exceptions.py:37
def nl_del(self, section, var)
Removes a variable from a namelist.
Definition: namelist.py:603
def nl_set_if_unset(self, section, var, data)
Sets the value of a namelist variable if it has no value.
Definition: namelist.py:654
def copy(self, other)
Returns a copy of self.
Definition: namelist.py:496
def nl_get
get the value of a variable from a namelist
Definition: namelist.py:635
def __str__(self)
synonym for make_namelist()
Definition: namelist.py:786
def nl_del_sect(self, section)
Definition: namelist.py:613
def join(self, others)
create array values by joining multiple Conf2Namelist objects
Definition: namelist.py:719
def __init__
Conf2Namelist constructor.
Definition: namelist.py:517
def from_fortnml(py)
Converts a fortran namelist value to a Python object.
Definition: namelist.py:121
def trait_each(self)
Iterates over variable,value tuples in the traits.
Definition: namelist.py:674
used to indicate namelist recursion
Definition: namelist.py:38
Time manipulation and other numerical routines.
Definition: numerics.py:1
def namelist_sorter(self, section)
return a sorting function for the variables in a namelist
Definition: namelist.py:843
def nl_set(self, section, var, data)
Sets a variable in a namelist.
Definition: namelist.py:586
def nl_section(self, args)
create namelists if they do not exist
Definition: namelist.py:578
Insert config file data into a Fortran namelist file.
Definition: namelist.py:154
def trait_set(self, var, value)
Sets a trait's value.
Definition: namelist.py:680
def make_namelist
generates the namelist as a string
Definition: namelist.py:793
section_sorter
The cmp function used to sort sections.
Definition: namelist.py:537
def nl_have_sect(self, section)
Definition: namelist.py:630
def nl_each(self, section)
Iterates over variable,value tuples in the given namelist.
Definition: namelist.py:666
Raised when an hwrf.namelist is asked for a key that does not exist.
Definition: exceptions.py:40
Exceptions raised by the hwrf package.
Definition: exceptions.py:1
nl
A dict of dicts used to store namelist information.
Definition: namelist.py:541
var_sorters
A dict mapping from section name to a cmp function used to sort namelist variables in that section...
Definition: namelist.py:538
def set_sorters(self, section_sorter, var_sorters)
sets the sorting algorithms for namelists and variables
Definition: namelist.py:856
def nl_have(self, section, var)
does this namelist have this variable?
Definition: namelist.py:618
def remove_traits(self)
Removes all traits.
Definition: namelist.py:779
def trait_have(self, var)
Returns True if the trait exists, and False otherwise.
Definition: namelist.py:705