HWRF  trunk@4391
confdoc.py
1 #! /usr/bin/env python
2 
3 ##@namespace confdoc
4 # Generates the doc/config-files.dox, which documents configuration files.
5 #
6 # Run by the documentation generator in @c sorc/doc/compile. Syntax:
7 #
8 # ../../ush/confdoc.py ../../parm/hwrf_basic.conf ... more ... > ../../doc/config-files.dox
9 #
10 # That will read in the listed conf files, process Doxygen-like
11 # comments, and generate multiple pages of documentation. There will
12 # be one page for each conf file, another page listing all sections
13 # and options, and a final top-level page
14 #
15 # The documentation comments are similar to the syntax Doxygen uses
16 # for Python, but with the addition of ";;" comments for documenting
17 # options on the same line they are defined:
18 #
19 # ## Brief description of section
20 # #
21 # # Detailed description of section
22 # [section]
23 # option1 = value ;; Brief description of option1
24 #
25 # ## Brief description of option2
26 # #
27 # # Detailed description of option2
28 # option2 = value
29 #
30 # The descriptions can contain the usual Doxygen and Markdown syntax.
31 #
32 # There are a number of pages and sections generated with the
33 # following anchors. These are the page anchors:
34 #
35 # + conf-files --- Main page that lists all subpages.
36 # + conf-options --- Subpage that contains all section and option
37 # documentation
38 # + conf-file-[filename] --- Page for the specified file. Any dots
39 # (".") are replaced with underscores ("_") in the filename.
40 #
41 # These are the section anchors:
42 #
43 # + conf-sec-runwrf --- Documentation section for the [runwrf] section
44 # + conf-sec-runwrf-wm3c_ranks --- Documentation subsection for the
45 # wm3c_ranks option in the [runwrf] section. Any percent signs
46 # ("%") in the option name are replaced with "-P-"
47 # + conf-[filename]-runwrf --- Documentation for the [runwrf] section
48 # in the specified file
49 # + conf-[filename]-runwrf-wm3c_ranks --- Documentation for the
50 # [runwrf] section's wm3c_ranks option in the specified file.
51 
52 import re, os, collections, sys, StringIO
53 
54 ########################################################################
55 
56 class docbase(object):
57  """!Stores documentation for all configuration options and sections."""
58  def __init__(self):
59  """!docbase constructor
60 
61  Initializes the documentation to an empty configuration suite.
62  No sections, no options, no files, etc."""
63  self.sections=dict()
64  self.secref=collections.defaultdict(list)
65  self.secrefset=collections.defaultdict(set)
66  self.secfile=collections.defaultdict(list)
67  self.secinc=collections.defaultdict(list)
68  self.seclist=list()
69  self.options=dict()
70  self.optbrief=dict()
71  self.secbrief=dict()
72  self.fileset=set()
73  self.filelist=list()
74  self.__subdoc=dict()
75  self.blocks=collections.defaultdict(list)
76 
77  def set_subdoc(self,basename,doc):
78  """!Sets the documentation object that will contain
79  file-specific information for the given file.
80 
81  @param basename The file basename
82  @param doc The documentation object, a subclass of docbase"""
83  if basename is not None and basename not in self.fileset:
84  self.fileset.add(basename)
85  self.filelist.append(basename)
86  self.__subdoc[basename]=doc
87 
88  ##@var sections
89  # Mapping of section name to description
90 
91  ##@var secref
92  # Mapping from section name to anchor name
93 
94  ##@var secfile
95  # Mapping from doxified filename to list of sections in that file
96 
97  ##@var secinc
98  # Mapping from section name to the list of sections that are \@inc
99  # included by that section
100 
101  ##@var seclist
102  # List of section names, in the order they were first seen
103 
104  ##@var options
105  # Mapping from option anchor name to the description of the option
106 
107  ##@var secbrief
108  # Mapping from section name to the section's brief description
109 
110  ##@var optbrief
111  # Mapping from option anchor to the option's brief description
112 
113  ##@var blocks
114  # Mapping from file basename to the list of documentation blocks in
115  # that file that were not associated with any section or option.
116  # These blocks are placed at the top of that file's documentation page.
117 
118  def fileanch(self,basename):
119  """!Returns the anchor for a configuration file.
120  @param basename the basename of the configuration file."""
121  return "conf-file-%s"%(basename.replace('.','_'),)
122 
123  def secanch(self,section,where='sec'):
124  """!Returns the anchor for a specified section.
125  @param section The conf file section name.
126  @param where Configuration override file name. Default: "sec"
127  which is the special name used for the page that stores
128  information about ALL configuration options."""
129  assert(section is not None)
130  return 'conf-%s-%s'%(where,section)
131 
132  def optanch(self,section,option,where='sec'):
133  """!Returns the anchor for a specified option.
134  @param section The conf file section name.
135  @param option The option name.
136  @param where Configuration override file name. Default: "sec"
137  which is the special name used for the page that stores
138  information about ALL configuration options."""
139  assert(section is not None)
140  assert(option is not None)
141  return( ('conf-%s-%s-%s'%(where,section,option)).replace('%','-P-') )
142 
143  def _secsec(self,section,brief,detail):
144  """!Generates a Doxygen section to represent a configuration
145  file section."""
146  anchor=self.secanch(section)
147  if brief:
148  out='@section %s Section [%s]\n%s\n\n'%(anchor,section,brief)
149  else:
150  out='@section %s Section [%s]\n\n'%(anchor,section)
151  if detail:
152  out+="%s\n\n"%(detail,)
153  return out
154 
155  def _optpar(self,section,name,ivalue,brief,detail=None,basename=None):
156  """!Generates a Doxygen subsection to represent a configuration
157  option in the specified section.
158 
159  @todo Allow documentation of options that are not in sections.
160 
161  @param section The section name. If this is None or blank
162  then an empty string is returned so that confdoc will not
163  abort when it sees an option outside of a section. (The
164  Python ConfigParser uses such options to set default
165  values.)
166  @param name The option name.
167  @param ivalue The first line of the option value, with an
168  ellipsis at the end if it is multiple lines.
169  @param brief The brief documentation, or None if it is absent.
170  @param detail The detailed documentation, or None if it is absent.
171  @param basename The conf file basename, or None if absent."""
172  if not section:
173  # Ignore options that are outside of sections since we
174  # have no good way of dealing with this in the
175  # documentation structure yet.
176  return ''
177  anchor=self.optanch(section,name)
178  if brief:
179  out="@subsection %s [%s] %s\n %s\n\n"%(anchor,section,name,brief)
180  else:
181  out="@subsection %s [%s] %s\n\n"%(anchor,section,name)
182  if ivalue is not None:
183  out+='@code{.conf}\n[%s]\n%s = %s\n\endcode\n\n'%(section,name,ivalue)
184  if detail:
185  out+="%s\n\n"%(detail,)
186  if basename:
187  out+="Defined in @ref %s\n\n"%(self.fileanch(basename),)
188  return out
189 
190  def make_brief(self,detail):
191  """!Given a detailed description for something that has no
192  brief description, return the brief description.
193  @param detail the detailed description
194  @returns A brief description, or None if no suitable
195  description was found."""
196  dlines=detail.split('\n')
197  for line in dlines:
198  tline=line.strip()
199  if len(tline)>2:
200  if len(dlines)>1:
201  return tline+'...'
202  return tline
203  return None
204 
205  def add_section(self,section,brief,detail,replace=None):
206  """!Adds documentation for a conf section
207 
208  @param section The conf section name
209  @param brief The brief documentation
210  @param detail The detailed documentation or None
211  @param replace If True, any existing documentation is
212  replaced. Otherwise, it is ignored."""
213  if replace is None:
214  replace=brief or detail
215  if detail and not brief:
216  brief=self.make_brief(detail)
217  replaceokay = replace and (brief or detail)
218  if section not in self.sections or replaceokay:
219  anchor=self.secanch(section)
220  self.sections[section]=self._secsec(section,brief,detail)
221  self.seclist.append(section)
222  if brief:
223  self.secbrief[section]=brief
224  self.secfile[anchor].append(section)
225 
226  def section_inc(self,section,inc):
227  """!Sets the \@inc list for a conf section
228 
229  @param section The conf section name
230  @param inc The contents of the \@inc= option"""
231  splat=re.split('\s*,\s*',inc)
232  if splat and splat[0]:
233  self.secinc[section]=splat
234  else:
235  self.secinc[section]=list()
236 
237  def file_block(self,basename,brief,detail):
238  """!Adds a documentation block that is not associated with any
239  section or option.
240 
241  @param basename the file that contains the block
242  @param brief the brief documentation
243  @param detail the detailed documentation"""
244  if basename not in self.fileset:
245  self.fileset.add(basename)
246  self.filelist.append(basename)
247  if brief or detail:
248  s= (brief if brief else '') + '\n\n' + \
249  (detail if detail else '')
250  self.blocks[basename].append(s)
251 
252  def add_option(self,section,option,ivalue,brief,detail,basename=None,replace=None):
253  """!Adds documentation for an option in a conf section
254 
255  @param section The conf section name
256  @param option The name of the option in that section
257  @param ivalue A shortened form of the option value
258  @param brief The brief documentation
259  @param detail The detailed documentation
260  @param basename The file basename
261  @param replace If True, any existing documentation is
262  replaced. Otherwise, it is ignored."""
263  if replace is None:
264  replace=brief or detail
265  if basename is not None and basename not in self.fileset:
266  self.fileset.add(basename)
267  self.filelist.append(basename)
268  anchor=self.optanch(section,option)
269  replaceokay = replace and (brief or detail)
270  if anchor in self.options and not replaceokay:
271  return
272  self.options[anchor]=self._optpar(section,option,ivalue,brief,detail,basename)
273  self.optbrief[anchor]=brief
274  if section and anchor not in self.secrefset[section]:
275  self.secref[section].append([anchor,option])
276  self.secrefset[section].add(anchor)
277 
278  def print_doc(self,s):
279  """!Writes Doxygen documentation to the stream "s" which is
280  assumed to be a StringIO.
281 
282  @param s a StringIO.StringIO to receive documentation."""
283  s.write('Documentation for all configuration files in the parm/ directory. Subpages include:\n\n');
284  s.write(' + @ref conf-options "All configuration sections and options."\n\n')
285  for basename in self.filelist:
286  #for basename,brdet in self.blocks.iteritems():
287  doxified=basename.replace('.','_')
288  s.write(' + @subpage conf-file-%s "File parm/%s"\n\n'%(
289  doxified,basename))
290 
291  for basename in self.filelist:
292  #for basename,brdet in self.blocks.iteritems():
293  doxified=basename.replace('.','_')
294  s.write('\n@page conf-file-%s File parm/%s\n\n'%(
295  doxified,basename))
296  if basename in self.blocks and self.blocks[basename]:
297  brdet=self.blocks[basename]
298  s.write('\n\n'.join(brdet) + '\n\n')
299  if basename in self.__subdoc:
300  self.__subdoc[basename].print_subdoc(s)
301  elif doxified in self.secfile:
302  s.write('Sections in this file:\n\n')
303  for sec in sorted(self.secfile[doxified]):
304  if sec in self.secbrief:
305  s.write(' + @ref conf-sec-%s "[%s]" --- %s\n\n'%(
306  sec,sec,self.secbrief[sec]))
307  else:
308  s.write(' + @ref conf-sec-%s "[%s]"\n\n'%(
309  sec,sec))
310 
311  for sec in self.secref:
312  if sec not in self.sections:
313  self.add_section(sec,None,None,replace=False)
314 
315  seclist=sorted(list(set(self.seclist)))
316 
317  s.write('@page conf-options All Configuration Options\n\n')
318  s.write('This page documents configuration options for all known sections:\n\n')
319  for sec in seclist:
320  if sec in self.secbrief:
321  s.write(' + @ref conf-sec-%s "[%s]" --- %s\n\n'
322  %(sec,sec,self.secbrief[sec]))
323  else:
324  s.write(' + @ref conf-sec-%s "[%s]"\n\n'%(sec,sec))
325 
326  for sec in seclist:
327  if sec in self.sections:
328  text=self.sections[sec]
329  s.write('\n\n%s\n\n'%(text,))
330  if self.secref[sec]:
331  s.write('\n\nOptions in this section:\n\n')
332  for fullopt,name in self.secref[sec]:
333  if fullopt in self.optbrief and self.optbrief[fullopt]:
334  s.write(' + @ref %s "%s" --- %s\n\n'%(
335  fullopt,name,self.optbrief[fullopt]))
336  else:
337  s.write(' + @ref %s "%s"\n\n'%(fullopt,name))
338  if sec in self.secinc and self.secinc[sec]:
339  s.write('Inherits from the following sections:\n\n')
340  for isec in self.secinc[sec]:
341  if isec in self.secbrief:
342  s.write(' + @ref conf-sec-%s "[%s]" --- %s\n\n'
343  %(isec,isec,self.secbrief[isec]))
344  else:
345  s.write(' + @ref conf-sec-%s "[%s]"\n\n'%(isec,isec))
346  s.write('\n\n')
347  for fullopt,name in self.secref[sec]:
348  if fullopt in self.options and self.options[fullopt]:
349  s.write(self.options[fullopt]+'\n\n')
350 
351 
352 ########################################################################
353 
355  """!Subclass of docbase for documenting files that override the
356  base configuration."""
357  def __init__(self,basename,parent,filepart=None,replace=False):
358  """!Class override constructor
359  @param basename The file basename
360  @param parent The parent docbase object that documents this
361  group of conf files, or all conf options.
362  @param filepart The modified filename for anchors.
363  This should be the basename with "." replaced by "_".
364  @param replace If True, replace existing documentation when
365  new values are found, otherwise ignore new docs"""
366  if not isinstance(parent,docbase):
367  raise TypeError('parent argument to override.__init__ must be a docbase')
368  self.__parent=parent
369  self.__basename=basename
370  self.__replace=replace
371  super(override,self).__init__()
372  self.__filepart=filepart
373  if not self.__filepart:
374  self.__filepart=basename.replace('.','_')
375  self.__parent.set_subdoc(basename,self)
376 
377  def secanch(self,section,where=None):
378  """!Returns the anchor for the specified section
379 
380  @param section The conf section name
381  @param where Optional: the conf file basename."""
382  if where is None:
383  where=self.__filepart
384  return super(override,self).secanch(section,where)
385 
386  def optanch(self,section,option,where=None):
387  """!Returns the anchor for a specified section and option
388 
389  @param option the option of interest
390  @param section the section that contains the option
391  @param Optional: the conf file basename"""
392  if where is None:
393  where=self.__filepart
394  return super(override,self).optanch(section,option,where)
395 
396 
397  def _secsec(self,section,brief,detail):
398  """!Generates the contents of the documentation section that
399  documents the specified conf section.
400 
401  @protected
402  @param section the conf section name
403  @param brief the brief documentation
404  @param detail the detailed documentation
405  @returns the section text"""
406  if not brief:
407  (brief,detail)=self.find_secdoc(section,detail)
408  return super(override,self)._secsec(section,brief,detail)
409 
410  def section_inc(self,section,inc):
411  """!Sets the \@inc list for a conf section
412 
413  @param section The conf section name
414  @param inc The contents of the \@inc= option"""
415  self.__parent.section_inc(section,inc)
416  super(override,self).section_inc(section,inc)
417 
418  def file_block(self,basename,brief,detail):
419  """!Adds a documentation block that is not associated with any
420  section or option.
421 
422  @param basename the file that contains the block
423  @param brief the brief documentation
424  @param detail the detailed documentation"""
425  self.__parent.file_block(basename,brief,detail)
426 
427  def add_section(self,section,brief,detail,replace=True):
428  """!Adds documentation for a conf section
429 
430  @param section The conf section name
431  @param brief The brief documentation
432  @param detail The detailed documentation or None
433  @param replace If True, any existing documentation is
434  replaced. Otherwise, it is ignored."""
435  self.__parent.add_section(section,brief,detail,self.__replace)
436  super(override,self).add_section(section,brief,detail,replace)
437 
438  def add_option(self,section,option,ivalue,brief,detail,basename=None,
439  replace=True):
440  """!Adds documentation for an option in a conf section
441 
442  @param section The conf section name
443  @param option The name of the option in that section
444  @param ivalue A shortened form of the option value
445  @param brief The brief documentation
446  @param detail The detailed documentation
447  @param basename The file basename
448  @param replace If True, any existing documentation is
449  replaced. Otherwise, it is ignored."""
450  self.__parent.add_option(
451  section,option,ivalue,brief,detail,basename,self.__replace)
452  super(override,self).add_option(
453  section,option,ivalue,brief,detail,basename,replace)
454 
455  def find_optbrief(self,section,option):
456  """!Finds the brief documentation for a section
457 
458  Checks first this documentation object, and then the parent,
459  searching for something that has documentation for the option.
460  @param section The conf section name
461  @param option The name of the option in that section"""
462  anchor=self.optanch(section,option)
463  if anchor in self.optbrief and self.optbrief[anchor]:
464  return self.optbrief[anchor]
465  anchor=self.__parent.optanch(section,option)
466  if anchor in self.__parent.optbrief and self.__parent.optbrief[anchor]:
467  return self.__parent.optbrief[anchor]
468  return None
469 
470  def find_secbrief(self,section):
471  """!Finds the brief documentation for a section
472 
473  Checks first this documentation object, and then the parent,
474  searching for something that has documentation for a section.
475  @param section The conf section name"""
476  if section in self.secbrief and self.secbrief[section]:
477  return self.secbrief[section]
478  if section in self.__parent.secbrief and \
479  self.__parent.secbrief[section]:
480  return self.__parent.secbrief[section]
481  if section=='namelist_outer':
482  print >>sys.stderr, 'Could not find %s in self or parent. Gave up.'%(section,)
483  return None
484 
485  def find_secdoc(self,section,detail):
486  """!Finds the brief and detailed documentation for a section
487 
488  Checks first this documentation object, and then the parent,
489  searching for something that has documentation for a section.
490  @param section The conf section name
491  @param detail detailed documentation, if available, and None otherwise
492  @returns A tuple (brief,detail) containing any documentation found."""
493  if section in self.secbrief and self.secbrief[section]:
494  if section=='namelist_outer':
495  print >>sys.stderr, 'Found %s in self'%(section,)
496  return (self.secbrief[section],detail)
497  if section in self.__parent.secbrief and self.__parent.secbrief[section]:
498  detail='See the @ref conf-sec-%s "main documentation for [%s]" '\
499  'for details.'%(section,section)
500  if section=='namelist_outer':
501  print >>sys.stderr, 'Found %s in parent'%(section,)
502  return (self.__parent.secbrief[section],detail)
503  if section=='namelist_outer':
504  print >>sys.stderr, 'Could not find %s in self or parent. Gave up.'%(section,)
505  return (None,None)
506 
507  def print_subdoc(self,s):
508  """!Prints the documentation to the specified stream
509  @param s The stream, ideally a StringIO.StringIO."""
510  s.write('This is a configuration override file.\n')
511  self.print_sec_opt(s)
512 
513  def print_sec_opt(self,s):
514  """!Prints the section and option part of the documentation to
515  the given stream.
516  @param s The stream, ideally a StringIO.StringIO."""
517  if not self.seclist:
518  s.write('This file does not override any options.\n\n')
519  return
520 
521  seclist=sorted(list(set(self.seclist)))
522  s.write('This file sets options in the following sections:\n\n')
523  for sec in seclist:
524  a=self.secanch(sec)
525  brief=self.find_secbrief(sec)
526  if brief:
527  s.write(' + @ref %s "[%s]" --- %s\n\n'%(a,sec,brief))
528  else:
529  s.write(' + @ref %s "[%s]"\n\n'%(a,sec))
530 
531  for sec,text in self.sections.iteritems():
532  s.write('\n\n%s\n\n'%(text,))
533  if self.secref[sec]:
534  s.write('\n\nOptions in this section:\n\n')
535  for fullopt,name in self.secref[sec]:
536  brief=self.find_optbrief(sec,name)
537  if brief:
538  s.write(' + @ref %s "%s" --- %s\n\n'%(
539  fullopt,name,brief))
540  else:
541  s.write(' + @ref %s "%s"\n\n'%(fullopt,name))
542  s.write('\n\n')
543  for fullopt,name in self.secref[sec]:
544  if fullopt in self.options and self.options[fullopt]:
545  s.write(self.options[fullopt]+'\n\n')
546 
547 ########################################################################
548 
550  """!Subclass of override, for documenting the core configuration
551  files."""
552  def __init__(self,basename,parent):
553  """!coredoc constructor
554  @param basename the file basename
555  @param parent The parent docbase"""
556  super(coredoc,self).__init__(basename,parent,None,True)
557  def print_subdoc(self,s):
558  """!Prints the documentation to the specified stream
559  @param s The stream, ideally a StringIO.StringIO."""
560  s.write('This is one of the core configuration files, read in by all HWRF configurations.\n')
561  self.print_sec_opt(s)
562 
563 ########################################################################
564 
565 class parsefile(object):
566  """!Config file parser. Parses comment blocks as described in the
567  confdoc."""
568  def __init__(self,filename,doc,maxread=500000):
569  """!Opens the specified file, prepares to parse. Only the
570  first maxread bytes are read.
571  @param filename the *.conf file to read
572  @param doc the docbase object to receive documentation
573  @param maxread maximum number of lines to read"""
574  self.doc=doc
575  with open(filename,'rt') as f:
576  self.lines=f.readlines(maxread)
577  self.iline=0
578  self.basename=os.path.basename(filename)
579  self.doxified=self.basename.replace('.','_')
580  self.brief=None
581  self.detail=None
582  self.section=None
583 
584  ##@var iline
585  # Current line number counting from 0
586 
587  ##@var basename
588  # Basename of the current file
589 
590  ##@var doxified
591  # The basename with "." replaced with "_"
592 
593  ##@var brief
594  # Brief portion of description that has not yet been assigned to
595  # an option or section.
596 
597  ##@var detail
598  # Detailed portion of description that has not yet been assigned to
599  # an option or section.
600 
601  ##@var section
602  # Section being parsed
603 
604  def eot(self):
605  """!Have we run out of lines to parse?"""
606  return self.iline>=len(self.lines)
607  def match(self,pattern):
608  """!Calls re.match(pattern,...) on the current line, returning
609  the result.
610  @param pattern argument to re.match: the pattern to match"""
611  if self.iline>=len(self.lines):
612  raise Exception('Internal error: line %d is beyond last line %d'%(
613  self.iline+1,len(self.lines)))
614  return re.match(pattern,self.lines[self.iline])
615  def parse(self):
616  """!Loops over all lines, parsing text and sending the result
617  to global variables."""
618  while not self.eot():
619 
620  # Is it a block comment start?
621  m=self.match('^#[#!]\s*(\S.*?)\s*$')
622  if m:
623  self.brief=m.groups()[0]
624  self.iline+=1
625  self.readblock()
626  continue
627 
628  # Is it a [section] start line?
629  m=self.match('^\s*\[\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*\].*$')
630  if m:
631  self.section=m.groups()[0]
632  assert(self.section)
633  if self.section:
634  assert(self.detail != 'None')
635  assert(self.brief != 'None')
636  self.doc.add_section(self.section,self.brief,self.detail)
637  self.iline+=1
638  self.brief=None
639  self.detail=None
640  continue
641 
642  # Is it an @inc= line?
643  m=self.match('^@inc\s*=\s*(\S+)\s*$')
644  if m and self.section:
645  self.doc.section_inc(self.section,m.groups()[0])
646  self.iline+=1
647  continue
648 
649  # Is it an option=value line?
650  m=self.match('''(?ix)
651 ^
652 (
653  [a-zA-Z][a-zA-Z0-9_%-]*
654 )
655 
656 \s* = \s*
657 
658 (
659  (?: [^"' \t]+
660  | \\047(?:[^\\047\\134]|\\134\\134|\\134\\047)*\\047
661  | \\042(?:[^\\042\\134]|\\134\\134|\\134\\042)*\\042
662  )
663 )?
664 (?:\s+
665  (;[;!]\s*.*?)\s*
666  | ;[^;!].*
667  | \s*
668 )
669 $''')
670  if m:
671  g=m.groups()
672  if len(g)>2 and g[2] and len(g[2])>2:
673  self.brief=g[2][2:].strip()
674  self.value=g[1]
675  self.iline+=1
676  self.readoption(g[0])
677  continue
678 
679  m=self.match('''(?ix)
680 ^
681 (
682  [a-zA-Z][a-zA-Z0-9_%-]*
683 )
684 
685 \s* = \s*
686 
687 (
688  (?: [^"' \t]+
689  | \\047(?:[^\\047\\134]|\\134\\134|\\134\\047)*\\047
690  | \\042(?:[^\\042\\134]|\\134\\134|\\134\\042)*\\042
691  )
692 )?
693 (?:\s+
694  ;[^;!].*
695  | \s*
696 )?
697 $''')
698  if m:
699  g=m.groups()
700  self.value=g[1]
701  self.iline+=1
702  self.readoption(g[0])
703  continue
704 
705 
706  # Otherwise, ignore the line and store unused
707  # documentation blocks:
708  if self.brief or self.detail:
709  self.doc.file_block(self.basename,self.brief,self.detail)
710  self.brief=None
711  self.detail=None
712 
713  self.iline+=1
714 
715  # Send any unused comment blocks:
716  if self.brief or self.detail:
717  self.doc.file_block(self.basename,self.brief,self.detail)
718 
719  def readoption(self,name):
720  """!Reads later lines of a multi-line option=value assignment
721  @param name the option name"""
722  (brief,detail)=(self.brief,self.detail)
723  self.brief=None
724  self.detail=None
725  nlines=1
726  while not self.eot():
727  if not self.match('^\s+(\S.*?)\s*$'):
728  break
729  self.iline+=1
730  nlines+=1
731  ivalue=self.value
732  if nlines>1: ivalue=ivalue+'...'
733  self.doc.add_option(self.section,name,ivalue,brief,detail,self.basename)
734 
735  def readblock(self):
736  """!Reads the second and later lines of a multi-line comment
737  block. Assumes data is already in the self.brief variable."""
738  isbrief=True
739  detail=list()
740  self.detail=None
741  if self.brief is None: self.brief=''
742  while not self.eot():
743  if self.match('^#+!?\s*$'):
744  if isbrief:
745  isbrief=False
746  else:
747  detail.append(' ')
748  self.iline+=1
749  continue
750  m=self.match('^#+!?\s?(.*)$')
751  if m:
752  doc=m.groups()[0]
753  if not doc: doc=' '
754  if isbrief:
755  self.brief=self.brief+' '+doc
756  else:
757  detail.append(doc)
758  self.iline+=1
759  continue
760 
761  # Do NOT consume any other type of line or the logic in
762  # parse() will break:
763  break
764  if not self.brief and detail:
765  self.brief=(detail[0]+'...').replace('\n','')
766  if not isbrief and detail:
767  self.detail='\n'.join(detail)
768  assert(self.detail != 'None')
769 
770 ########################################################################
771 
772 def main(args):
773  """!Main program for confdoc. See the confdoc documentation for
774  details."""
775  s=StringIO.StringIO()
776  doc=docbase()
777  for arg in args:
778  bn=os.path.basename(arg)
779  if bn=='hwrf_basic.conf' or bn=='hwrf.conf' or bn=='hwrf_input.conf':
780  subdoc=coredoc(bn,doc)
781  else:
782  subdoc=override(bn,doc)
783  parsefile(arg,subdoc).parse()
784  s.write('/** @page conf-files All Configuration Files\n')
785  doc.print_doc(s)
786  out=s.getvalue()
787  s.close()
788  out=out.replace('*/','* /')+'\n\n*/'
789  print '/* WARNING: DO NOT EDIT THIS FILE.\nIt is automatically generated from the parm *.conf files.\nEdit those files instead.\n*/\n\n'
790  print out
791 
792 if __name__=='__main__':
793  main(sys.argv[1:])
def __init__(self)
docbase constructor
Definition: confdoc.py:58
def add_section
Adds documentation for a conf section.
Definition: confdoc.py:427
secref
Mapping from section name to anchor name.
Definition: confdoc.py:64
def section_inc(self, section, inc)
Sets the @inc list for a conf section.
Definition: confdoc.py:410
def fileanch(self, basename)
Returns the anchor for a configuration file.
Definition: confdoc.py:118
basename
Basename of the current file.
Definition: confdoc.py:578
def _optpar
Generates a Doxygen subsection to represent a configuration option in the specified section...
Definition: confdoc.py:155
seclist
List of section names, in the order they were first seen.
Definition: confdoc.py:68
blocks
Mapping from file basename to the list of documentation blocks in that file that were not associated ...
Definition: confdoc.py:75
def parse(self)
Loops over all lines, parsing text and sending the result to global variables.
Definition: confdoc.py:615
Subclass of override, for documenting the core configuration files.
Definition: confdoc.py:549
iline
Current line number counting from 0.
Definition: confdoc.py:577
def readblock(self)
Reads the second and later lines of a multi-line comment block.
Definition: confdoc.py:735
def make_brief(self, detail)
Given a detailed description for something that has no brief description, return the brief descriptio...
Definition: confdoc.py:190
detail
Detailed portion of description that has not yet been assigned to an option or section.
Definition: confdoc.py:581
options
Mapping from option anchor name to the description of the option.
Definition: confdoc.py:69
def print_subdoc(self, s)
Prints the documentation to the specified stream.
Definition: confdoc.py:557
secbrief
Mapping from section name to the section's brief description.
Definition: confdoc.py:71
def add_option
Adds documentation for an option in a conf section.
Definition: confdoc.py:252
def __init__(self, basename, parent)
coredoc constructor
Definition: confdoc.py:552
def secanch
Returns the anchor for a specified section.
Definition: confdoc.py:123
def find_secdoc(self, section, detail)
Finds the brief and detailed documentation for a section.
Definition: confdoc.py:485
def print_sec_opt(self, s)
Prints the section and option part of the documentation to the given stream.
Definition: confdoc.py:513
secfile
Mapping from doxified filename to list of sections in that file.
Definition: confdoc.py:66
sections
Mapping of section name to description.
Definition: confdoc.py:63
doxified
The basename with "." replaced with "_".
Definition: confdoc.py:579
def match(self, pattern)
Calls re.match(pattern,...) on the current line, returning the result.
Definition: confdoc.py:607
def secanch
Returns the anchor for the specified section.
Definition: confdoc.py:377
def find_secbrief(self, section)
Finds the brief documentation for a section.
Definition: confdoc.py:470
def main(args)
Main program for confdoc.
Definition: confdoc.py:772
def eot(self)
Have we run out of lines to parse?
Definition: confdoc.py:604
optbrief
Mapping from option anchor to the option's brief description.
Definition: confdoc.py:70
brief
Brief portion of description that has not yet been assigned to an option or section.
Definition: confdoc.py:580
Stores documentation for all configuration options and sections.
Definition: confdoc.py:56
def section_inc(self, section, inc)
Sets the @inc list for a conf section.
Definition: confdoc.py:226
def file_block(self, basename, brief, detail)
Adds a documentation block that is not associated with any section or option.
Definition: confdoc.py:237
def file_block(self, basename, brief, detail)
Adds a documentation block that is not associated with any section or option.
Definition: confdoc.py:418
section
Section being parsed.
Definition: confdoc.py:582
def add_option
Adds documentation for an option in a conf section.
Definition: confdoc.py:439
def print_subdoc(self, s)
Prints the documentation to the specified stream.
Definition: confdoc.py:507
def add_section
Adds documentation for a conf section.
Definition: confdoc.py:205
def print_doc(self, s)
Writes Doxygen documentation to the stream "s" which is assumed to be a StringIO. ...
Definition: confdoc.py:278
def _secsec(self, section, brief, detail)
Generates the contents of the documentation section that documents the specified conf section...
Definition: confdoc.py:397
def optanch
Returns the anchor for a specified option.
Definition: confdoc.py:132
def set_subdoc(self, basename, doc)
Sets the documentation object that will contain file-specific information for the given file...
Definition: confdoc.py:77
def readoption(self, name)
Reads later lines of a multi-line option=value assignment.
Definition: confdoc.py:719
secinc
Mapping from section name to the list of sections that are @inc included by that section.
Definition: confdoc.py:67
def _secsec(self, section, brief, detail)
Generates a Doxygen section to represent a configuration file section.
Definition: confdoc.py:143
Subclass of docbase for documenting files that override the base configuration.
Definition: confdoc.py:354
Config file parser.
Definition: confdoc.py:565
def __init__
Class override constructor.
Definition: confdoc.py:357
def __init__
Opens the specified file, prepares to parse.
Definition: confdoc.py:568
def find_optbrief(self, section, option)
Finds the brief documentation for a section.
Definition: confdoc.py:455
def optanch
Returns the anchor for a specified section and option.
Definition: confdoc.py:386