RN rnmake  3.0.0
pydocmk.py
1 #!/usr/bin/env python
2 # Package: RN Makefile System Utility
3 # File: pydocmk.py
4 # Desc: Make python documentation for python [sub]package.
5 # Usage: pydocmk.sh -d <docroot> setup
6 #
7 # /*! \file */
8 # /*! \cond RNMAKE_DOXY*/
9 
10 import sys
11 import os
12 import time
13 #import glob
14 import pydoc
15 import getopt
16 
17 #------------------------------------------------------------------------------
18 # Globals
19 #------------------------------------------------------------------------------
20 
21 DocRoot = None # document install root directory path
22 PkgRoot = None # RN package root
23 PyDocInfo = None # pydoc meta info from setup.py
24 PkgInfo = None # package info from setup.py
25 
26 # HTML @variable@ substitution names to values dictionary
27 HtmlVars = {
28  'PKG_NAME': '',
29  'PKG_VER': '',
30  'BRIEF': '',
31  'LONG_DESC': '',
32  'AUTHOR': '',
33  'EMAIL': '',
34  'ORG_FQ': '',
35  'ORG_INITIALS': '',
36  'ORG_URL': '',
37  'MOD_URL_LIST': [],
38  'MOD_LIST_ITER': None,
39 }
40 
41 
42 #------------------------------------------------------------------------------
43 # Template Variable '@variable@' AtAt Processing
44 #------------------------------------------------------------------------------
45 
46 import shutil
47 import re
48 
49 _reAtAt = re.compile('@([a-zA-Z_]\w*)@|@([a-zA-Z_]\w*):(.+)@')
50 _Now = time.localtime()
51 
52 AtAtBuiltIns = {
53  'THIS_YEAR': repr(_Now.tm_year),
54  'THIS_DATE': "%d.%02d.%02d" % (_Now.tm_year, _Now.tm_mon, _Now.tm_mday),
55  'THIS_TIME': "%02d:%02d:%02d" % (_Now.tm_hour, _Now.tm_min, _Now.tm_sec),
56 }
57 
58 #--
59 def AtAtIdentifier(match):
60  if match.lastindex == 1:
61  return (match.group(1), None)
62  if match.lastindex == 3:
63  return (match.group(2), match.group(3))
64  else:
65  return (None, None)
66 
67 #--
68 def AtAtWriteValAtomic(fp, val, fmt):
69  if fmt:
70  fp.write(fmt % (val))
71  elif type(val) == str:
72  fp.write(val)
73  else:
74  fp.write(repr(val))
75 
76 #--
77 def AtAtWriteValList(fp, valList, fmt):
78  for val in valList:
79  AtAtWriteValAtomic(fp, val, fmt)
80  if not fmt:
81  fp.write(' ')
82 
83 #--
84 def AtAtWriteVal(fp, id, valDict, fmt=None):
85  val = valDict[id]
86  if val is None:
87  pass
88  elif callable(val):
89  val(fp, id, valDict, fmt)
90  elif type(val) == list:
91  AtAtWriteValList(fp, val, fmt)
92  else:
93  AtAtWriteValAtomic(fp, val, fmt)
94 
95 #--
96 def AtAtReplace(fileNameIn, userDict, bReplace=True):
97  fileNameTmp = fileNameIn + '.tmp'
98  fpSrc = open(fileNameIn, 'r')
99  fpTmp = open(fileNameTmp, 'w')
100  lineNum = 1
101  line = fpSrc.readline()
102  while line:
103  #print "[%d] %s" % (lineNum, line),
104  m = 0
105  for match in _reAtAt.finditer(line):
106  #print match.group(0), match.start(), match.end()
107  fpTmp.write(line[m:match.start()])
108  id,fmt = AtAtIdentifier(match)
109  if userDict.has_key(id):
110  AtAtWriteVal(fpTmp, id, userDict, fmt)
111  elif AtAtBuiltIns.has_key(id):
112  AtAtWriteVal(fpTmp, id, AtAtBuiltIns, fmt)
113  else:
114  AtAtWarning(fileNameIn, lineNum, match.start(), id,
115  "unknown identifier")
116  fpTmp.write(match.group(0))
117  m = match.end()
118  if m < len(line):
119  fpTmp.write(line[m:])
120  lineNum += 1
121  line = fpSrc.readline()
122  fpSrc.close()
123  fpTmp.close()
124  if bReplace:
125  os.rename(fileNameTmp, fileNameIn)
126  return fileNameIn
127  else:
128  return fileNameTmp
129 
130 #--
131 def AtAtWarning(fileName, lineNum, colNum, *args):
132  """ Print AtAt warning.
133 
134  Parameters:
135  *args - List of warning message arguments.
136  """
137  wmsg = "Warning: %s[%d,%d]" % (fileName, lineNum, colNum)
138  for a in args:
139  wmsg += ": %s" %(a)
140  print wmsg
141 
142 
143 #------------------------------------------------------------------------------
144 # Document Processing
145 #------------------------------------------------------------------------------
146 
147 #--
148 def AtAtCbModUrlListIter(fp, id, varDict, fmt):
149  _icnt = 0
150  for _ivar in varDict['MOD_URL_LIST']:
151  s = eval(fmt)
152  fp.write(s)
153  _icnt += 1
154 
155 #--
156 def MakeEnv(docRoot, modSetup):
157  global DocRoot, PkgRoot, PyDocInfo, PkgInfo
158  dname = os.path.dirname(modSetup)
159  if dname:
160  sys.path = [dname] + sys.path
161  else:
162  sys.path = ['.'] + sys.path
163  try:
164  exec("import "+modSetup+" as setup")
165  except ImportError:
166  PrintUsageErr("%s: setup module not found - cannot import" % (modSetup))
167  sys.exit(2)
168  now = time.localtime()
169  DocRoot = docRoot
170  PkgRoot = setup.pkgroot
171  PyDocInfo = setup.PyDocInfo
172  PkgInfo = setup.PkgInfo
173  HtmlVars['PKG_NAME'] = PkgInfo['name'] # required
174  HtmlVars['PKG_VER'] = PkgInfo['version'] # required
175  HtmlVars['BRIEF'] = PkgInfo.get('description', '')
176  HtmlVars['LONG_DESC'] = PkgInfo.get('long_description', '')
177  HtmlVars['AUTHOR'] = PkgInfo.get('author', 'RoadNarrows')
178  HtmlVars['EMAIL'] = PkgInfo.get('author_email','oneway@roadnarrows.com')
179  HtmlVars['ORG_FQ'] = PkgInfo.get('maintainer', 'RoadNarrows LLC')
180  HtmlVars['ORG_INITIALS'] = PyDocInfo.get('org_initials', 'RN')
181  HtmlVars['ORG_URL'] = PkgInfo.get('url', 'http://www.roadnarrows.com/')
182  HtmlVars['MOD_LIST_ITER'] = AtAtCbModUrlListIter
183  for varname,imgfile in PyDocInfo['images'].iteritems():
184  HtmlVars[varname] = imgfile
185 
186 #--
187 def MakeDirs():
188  if not os.path.exists(DocRoot):
189  os.makedirs(DocRoot, mode=0775)
190  imgdir = DocRoot+os.sep+'images'
191  if not os.path.exists(imgdir):
192  os.mkdir(DocRoot+os.sep+'images', 0775)
193 
194 #--
195 def CopyImages():
196  srcdir = PyDocInfo['images_dir']
197  if not srcdir:
198  return
199  dstdir = DocRoot+os.sep+'images'+os.sep
200  for varname,imgfile in PyDocInfo['images'].iteritems():
201  shutil.copy(srcdir+os.sep+imgfile, dstdir)
202  print " Copied images."
203 
204 #--
205 def GetCbWalk(pyfilelist, dirname, fnames):
206  excludes = ['.svn', '.deps' 'obj']
207  for sEx in excludes:
208  if dirname.find(sEx) != -1:
209  return
210  pyfilelist += [dirname]
211  for f in fnames:
212  if len(f) > 3 and f[-3:] == '.py':
213  pyfilelist += [dirname+os.sep+f]
214 
215 #--
216 def GetPythonFileList(modname):
217  pyfilelist = []
218  os.path.walk(modname, GetCbWalk, pyfilelist)
219  return pyfilelist
220 
221 #--
222 def MakeHtmlBatch(modbatch, htmlfilelist):
223  if not modbatch:
224  return 0
225  cmd = "pydoc -w"
226  for modpath in modbatch:
227  cmd += ' '+modpath
228  ec = os.system(cmd)
229  if ec == 0:
230  for modpath in modbatch:
231  htmlfilelist += [modpath+'.html']
232  return ec
233 
234 #--
235 def MakeHtmlPkgDocs():
236  #pydocumater = pydoc.HTMLDoc() # not well documented
237  for pkgdir in PkgInfo['package_dir'].itervalues():
238  dname = os.path.dirname(pkgdir)
239  modname = os.path.basename(pkgdir)
240  HtmlVars['MOD_URL_LIST'] += [modname + '.html']
241  if dname:
242  curdir = os.getcwd()
243  os.chdir(dname)
244  else:
245  curdir = os.curdir
246  #pydoc.writedocs('.', modname+'.') #os.curdir) # strange behavior
247  htmlfilelist = []
248  pyfilelist = GetPythonFileList(modname)
249  modbatch = []
250  for pyfile in pyfilelist:
251  bname=os.path.basename(pyfile)
252  if bname[0] == '_':
253  continue
254  modpath = pyfile.replace(os.sep, '.')
255  modpath = modpath.replace('.py', '')
256  if len(modbatch) < 8: # batch up to speed doc building
257  modbatch += [modpath]
258  else:
259  MakeHtmlBatch(modbatch, htmlfilelist)
260  modbatch = []
261  if len(modbatch) > 0:
262  MakeHtmlBatch(modbatch, htmlfilelist)
263  modbatch = []
264  os.chdir(curdir)
265  for html in htmlfilelist:
266  if dname:
267  srcfile = dname + os.sep + html
268  else:
269  srcfile = html
270  dstfile = DocRoot+os.sep+html
271  try:
272  os.rename(srcfile, DocRoot+os.sep+html)
273  except OSError:
274  print >>sys.stderr, "%s -> %s: cannot copy" % (srcfile, dstfile)
275 
276 #--
277 def MakeHtmlIndex():
278  postfile = AtAtReplace(PyDocInfo['index_template'], HtmlVars, False)
279  os.rename(postfile, DocRoot+os.sep+'index.html')
280  print ' Wrote index.html'
281 
282 #--
283 def MakePyDoc(docRoot, modSetup):
284  MakeEnv(docRoot, modSetup)
285  MakeDirs()
286  CopyImages()
287  MakeHtmlPkgDocs()
288  MakeHtmlIndex()
289 
290 
291 #------------------------------------------------------------------------------
292 # Main Execution
293 #------------------------------------------------------------------------------
294 
295 _Argv0 = __file__
296 
297 #--
298 class Usage(Exception):
299  """ Command-Line Options Usage Exception Class. """
300  def __init__(self, msg):
301  self.msg = msg
302 
303 #--
304 def PrintUsageErr(emsg):
305  """ Print Error Usage Message. """
306  if emsg:
307  print "%s: %s" % (_Argv0, emsg)
308  else:
309  print "%s: error" % (_Argv0)
310  print "Try '%s --help' for more information." % (_Argv0)
311 
312 #--
313 def PrintUsage():
314  """ Print Command-Line Usage Message """
315  print """"
316 usage: %s [OPTIONS] <setup_py_file>
317 
318  %s --help
319  """ % (_Argv0, _Argv0)
320  print """Options and arguments:
321 -d, --docroot=<dir> : Generated HTML documentation root directory
322  --vpath=<path> : Library virtual path
323 
324 -h, --help : Display this help and exit.
325  """
326 
327 #--
328 def GetOptions(argv=None, **kwargs):
329  """ Get Main Options and Arguments """
330  global _Argv0
331 
332  if argv is None:
333  argv = sys.argv
334 
335  _Argv0 = kwargs.get('argv0', __file__)
336 
337  # defaults
338  kwargs['vpath'] = None
339  kwargs['debug'] = 0
340 
341  # parse command-line options
342  try:
343  try:
344  opts, args = getopt.getopt(argv[1:], "?hd:",
345  ['help', 'docroot=', 'vpath=', ''])
346  except getopt.error, msg:
347  raise Usage(msg)
348  for opt, optarg in opts:
349  if opt in ('-h', '--help', '-?'):
350  PrintUsage()
351  sys.exit(0)
352  elif opt in ('-d', '--docroot'):
353  kwargs['docroot'] = optarg
354  elif opt in ('--vpath'):
355  kwargs['vpath'] = optarg
356  except Usage, err:
357  PrintUsageErr(err.msg)
358  sys.exit(2)
359 
360  if len(args) < 1:
361  PrintUsageErr("No input setup.py file specified")
362  sys.exit(2)
363  else:
364  kwargs['setup'] = args[0]
365 
366  return kwargs
367 
368 #--
369 def Main(argv=None, **kwargs):
370  """ Main """
371  global _Argv0
372  kwargs = GetOptions(argv, **kwargs)
373  if kwargs['vpath'] is not None:
374  ldpath = os.getenv('LD_LIBRARY_PATH')
375  if ldpath:
376  ldpath = kwargs['vpath'] + ':' + ldpath
377  else:
378  ldpath = kwargs['vpath']
379  os.putenv('LD_LIBRARY_PATH', ldpath)
380  MakePyDoc(kwargs['docroot'], kwargs['setup'])
381 
382 #--
383 # Execute
384 #
385 sys.exit( Main() )
386 
387 #/*! \endcond RNMAKE_DOXY */