52 import re, os, collections, sys, StringIO
57 """!Stores documentation for all configuration options and sections."""
59 """!docbase constructor
61 Initializes the documentation to an empty configuration suite.
62 No sections, no options, no files, etc."""
64 self.
secref=collections.defaultdict(list)
65 self.
secrefset=collections.defaultdict(set)
66 self.
secfile=collections.defaultdict(list)
67 self.
secinc=collections.defaultdict(list)
75 self.
blocks=collections.defaultdict(list)
78 """!Sets the documentation object that will contain
79 file-specific information for the given file.
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)
119 """!Returns the anchor for a configuration file.
120 @param basename the basename of the configuration file."""
121 return "conf-file-%s"%(basename.replace(
'.',
'_'),)
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)
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-') )
143 def _secsec(self,section,brief,detail):
144 """!Generates a Doxygen section to represent a configuration
148 out=
'@section %s Section [%s]\n%s\n\n'%(anchor,section,brief)
150 out=
'@section %s Section [%s]\n\n'%(anchor,section)
152 out+=
"%s\n\n"%(detail,)
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.
159 @todo Allow documentation of options that are not in sections.
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
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."""
177 anchor=self.
optanch(section,name)
179 out=
"@subsection %s [%s] %s\n %s\n\n"%(anchor,section,name,brief)
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)
185 out+=
"%s\n\n"%(detail,)
187 out+=
"Defined in @ref %s\n\n"%(self.
fileanch(basename),)
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')
206 """!Adds documentation for a conf section
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."""
214 replace=brief
or detail
215 if detail
and not brief:
217 replaceokay = replace
and (brief
or detail)
218 if section
not in self.
sections or replaceokay:
221 self.seclist.append(section)
224 self.
secfile[anchor].append(section)
227 """!Sets the \@inc list for a conf section
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
235 self.
secinc[section]=list()
238 """!Adds a documentation block that is not associated with any
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)
248 s= (brief
if brief
else '') +
'\n\n' + \
249 (detail
if detail
else '')
250 self.
blocks[basename].append(s)
252 def add_option(self,section,option,ivalue,brief,detail,basename=None,replace=None):
253 """!Adds documentation for an option in a conf section
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."""
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:
272 self.
options[anchor]=self.
_optpar(section,option,ivalue,brief,detail,basename)
274 if section
and anchor
not in self.
secrefset[section]:
275 self.
secref[section].append([anchor,option])
279 """!Writes Doxygen documentation to the stream "s" which is
280 assumed to be a StringIO.
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')
287 doxified=basename.replace(
'.',
'_')
288 s.write(
' + @subpage conf-file-%s "File parm/%s"\n\n'%(
293 doxified=basename.replace(
'.',
'_')
294 s.write(
'\n@page conf-file-%s File parm/%s\n\n'%(
297 brdet=self.
blocks[basename]
298 s.write(
'\n\n'.join(brdet) +
'\n\n')
300 self.
__subdoc[basename].print_subdoc(s)
302 s.write(
'Sections in this file:\n\n')
303 for sec
in sorted(self.
secfile[doxified]):
305 s.write(
' + @ref conf-sec-%s "[%s]" --- %s\n\n'%(
308 s.write(
' + @ref conf-sec-%s "[%s]"\n\n'%(
315 seclist=sorted(list(set(self.
seclist)))
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')
321 s.write(
' + @ref conf-sec-%s "[%s]" --- %s\n\n'
324 s.write(
' + @ref conf-sec-%s "[%s]"\n\n'%(sec,sec))
329 s.write(
'\n\n%s\n\n'%(text,))
331 s.write(
'\n\nOptions in this section:\n\n')
332 for fullopt,name
in self.
secref[sec]:
334 s.write(
' + @ref %s "%s" --- %s\n\n'%(
335 fullopt,name,self.
optbrief[fullopt]))
337 s.write(
' + @ref %s "%s"\n\n'%(fullopt,name))
339 s.write(
'Inherits from the following sections:\n\n')
340 for isec
in self.
secinc[sec]:
342 s.write(
' + @ref conf-sec-%s "[%s]" --- %s\n\n'
345 s.write(
' + @ref conf-sec-%s "[%s]"\n\n'%(isec,isec))
347 for fullopt,name
in self.
secref[sec]:
349 s.write(self.
options[fullopt]+
'\n\n')
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')
375 self.__parent.set_subdoc(basename,self)
378 """!Returns the anchor for the specified section
380 @param section The conf section name
381 @param where Optional: the conf file basename."""
384 return super(override,self).
secanch(section,where)
387 """!Returns the anchor for a specified section and option
389 @param option the option of interest
390 @param section the section that contains the option
391 @param Optional: the conf file basename"""
394 return super(override,self).
optanch(section,option,where)
398 """!Generates the contents of the documentation section that
399 documents the specified conf section.
402 @param section the conf section name
403 @param brief the brief documentation
404 @param detail the detailed documentation
405 @returns the section text"""
408 return super(override,self).
_secsec(section,brief,detail)
411 """!Sets the \@inc list for a conf section
413 @param section The conf section name
414 @param inc The contents of the \@inc= option"""
415 self.__parent.section_inc(section,inc)
419 """!Adds a documentation block that is not associated with any
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)
428 """!Adds documentation for a conf section
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)
438 def add_option(self,section,option,ivalue,brief,detail,basename=None,
440 """!Adds documentation for an option in a conf section
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)
453 section,option,ivalue,brief,detail,basename,replace)
456 """!Finds the brief documentation for a section
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)
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]
471 """!Finds the brief documentation for a section
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"""
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,)
486 """!Finds the brief and detailed documentation for a section
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."""
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,)
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')
514 """!Prints the section and option part of the documentation to
516 @param s The stream, ideally a StringIO.StringIO."""
518 s.write(
'This file does not override any options.\n\n')
521 seclist=sorted(list(set(self.
seclist)))
522 s.write(
'This file sets options in the following sections:\n\n')
527 s.write(
' + @ref %s "[%s]" --- %s\n\n'%(a,sec,brief))
529 s.write(
' + @ref %s "[%s]"\n\n'%(a,sec))
531 for sec,text
in self.sections.iteritems():
532 s.write(
'\n\n%s\n\n'%(text,))
534 s.write(
'\n\nOptions in this section:\n\n')
535 for fullopt,name
in self.
secref[sec]:
538 s.write(
' + @ref %s "%s" --- %s\n\n'%(
541 s.write(
' + @ref %s "%s"\n\n'%(fullopt,name))
543 for fullopt,name
in self.
secref[sec]:
545 s.write(self.
options[fullopt]+
'\n\n')
550 """!Subclass of override, for documenting the core configuration
553 """!coredoc constructor
554 @param basename the file basename
555 @param parent The parent docbase"""
556 super(coredoc,self).
__init__(basename,parent,
None,
True)
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')
566 """!Config file parser. Parses comment blocks as described in the
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"""
575 with open(filename,
'rt')
as f:
576 self.
lines=f.readlines(maxread)
605 """!Have we run out of lines to parse?"""
608 """!Calls re.match(pattern,...) on the current line, returning
610 @param pattern argument to re.match: the pattern to match"""
612 raise Exception(
'Internal error: line %d is beyond last line %d'%(
614 return re.match(pattern,self.
lines[self.
iline])
616 """!Loops over all lines, parsing text and sending the result
617 to global variables."""
618 while not self.
eot():
621 m=self.
match(
'^#[#!]\s*(\S.*?)\s*$')
623 self.
brief=m.groups()[0]
629 m=self.
match(
'^\s*\[\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*\].*$')
634 assert(self.
detail !=
'None')
635 assert(self.
brief !=
'None')
643 m=self.
match(
'^@inc\s*=\s*(\S+)\s*$')
645 self.doc.section_inc(self.
section,m.groups()[0])
650 m=self.
match(
'''(?ix)
653 [a-zA-Z][a-zA-Z0-9_%-]*
660 | \\047(?:[^\\047\\134]|\\134\\134|\\134\\047)*\\047
661 | \\042(?:[^\\042\\134]|\\134\\134|\\134\\042)*\\042
672 if len(g)>2
and g[2]
and len(g[2])>2:
673 self.
brief=g[2][2:].strip()
679 m=self.
match(
'''(?ix)
682 [a-zA-Z][a-zA-Z0-9_%-]*
689 | \\047(?:[^\\047\\134]|\\134\\134|\\134\\047)*\\047
690 | \\042(?:[^\\042\\134]|\\134\\134|\\134\\042)*\\042
720 """!Reads later lines of a multi-line option=value assignment
721 @param name the option name"""
726 while not self.
eot():
727 if not self.
match(
'^\s+(\S.*?)\s*$'):
732 if nlines>1: ivalue=ivalue+
'...'
733 self.doc.add_option(self.
section,name,ivalue,brief,detail,self.
basename)
736 """!Reads the second and later lines of a multi-line comment
737 block. Assumes data is already in the self.brief variable."""
742 while not self.
eot():
743 if self.
match(
'^#+!?\s*$'):
750 m=self.
match(
'^#+!?\s?(.*)$')
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')
773 """!Main program for confdoc. See the confdoc documentation for
775 s=StringIO.StringIO()
778 bn=os.path.basename(arg)
779 if bn==
'hwrf_basic.conf' or bn==
'hwrf.conf' or bn==
'hwrf_input.conf':
784 s.write(
'/** @page conf-files All Configuration Files\n')
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'
792 if __name__==
'__main__':
def __init__(self)
docbase constructor
def add_section
Adds documentation for a conf section.
secref
Mapping from section name to anchor name.
def section_inc(self, section, inc)
Sets the @inc list for a conf section.
def fileanch(self, basename)
Returns the anchor for a configuration file.
basename
Basename of the current file.
def _optpar
Generates a Doxygen subsection to represent a configuration option in the specified section...
seclist
List of section names, in the order they were first seen.
blocks
Mapping from file basename to the list of documentation blocks in that file that were not associated ...
def parse(self)
Loops over all lines, parsing text and sending the result to global variables.
Subclass of override, for documenting the core configuration files.
iline
Current line number counting from 0.
def readblock(self)
Reads the second and later lines of a multi-line comment block.
def make_brief(self, detail)
Given a detailed description for something that has no brief description, return the brief descriptio...
detail
Detailed portion of description that has not yet been assigned to an option or section.
options
Mapping from option anchor name to the description of the option.
def print_subdoc(self, s)
Prints the documentation to the specified stream.
secbrief
Mapping from section name to the section's brief description.
def add_option
Adds documentation for an option in a conf section.
def __init__(self, basename, parent)
coredoc constructor
def secanch
Returns the anchor for a specified section.
def find_secdoc(self, section, detail)
Finds the brief and detailed documentation for a section.
def print_sec_opt(self, s)
Prints the section and option part of the documentation to the given stream.
secfile
Mapping from doxified filename to list of sections in that file.
sections
Mapping of section name to description.
doxified
The basename with "." replaced with "_".
def match(self, pattern)
Calls re.match(pattern,...) on the current line, returning the result.
def secanch
Returns the anchor for the specified section.
def find_secbrief(self, section)
Finds the brief documentation for a section.
def main(args)
Main program for confdoc.
def eot(self)
Have we run out of lines to parse?
optbrief
Mapping from option anchor to the option's brief description.
brief
Brief portion of description that has not yet been assigned to an option or section.
Stores documentation for all configuration options and sections.
def section_inc(self, section, inc)
Sets the @inc list for a conf section.
def file_block(self, basename, brief, detail)
Adds a documentation block that is not associated with any section or option.
def file_block(self, basename, brief, detail)
Adds a documentation block that is not associated with any section or option.
section
Section being parsed.
def add_option
Adds documentation for an option in a conf section.
def print_subdoc(self, s)
Prints the documentation to the specified stream.
def add_section
Adds documentation for a conf section.
def print_doc(self, s)
Writes Doxygen documentation to the stream "s" which is assumed to be a StringIO. ...
def _secsec(self, section, brief, detail)
Generates the contents of the documentation section that documents the specified conf section...
def optanch
Returns the anchor for a specified option.
def set_subdoc(self, basename, doc)
Sets the documentation object that will contain file-specific information for the given file...
def readoption(self, name)
Reads later lines of a multi-line option=value assignment.
secinc
Mapping from section name to the list of sections that are @inc included by that section.
def _secsec(self, section, brief, detail)
Generates a Doxygen section to represent a configuration file section.
Subclass of docbase for documenting files that override the base configuration.
def __init__
Class override constructor.
def __init__
Opens the specified file, prepares to parse.
def find_optbrief(self, section, option)
Finds the brief documentation for a section.
def optanch
Returns the anchor for a specified section and option.