0001"""
0002Generate Python documentation and other stuff.
0003
0004
0005"""
0006
0007import sys
0008import os
0009import os.path as path
0010import inspect
0011import kid
0012
0013import pudge
0014import pudge.browser as browser
0015import pudge.colorizer as colorizer
0016import pudge.rst as rst
0017
0018from pkg_resources import resource_filename
0019
0020class Generator(object):
0021 """
0022 Takes configuration options and performs file generation.
0023 """
0024
0025 file_extension = '.html'
0026 title = 'Project Name'
0027 dest = '.'
0028 force = 1
0029 modules = []
0030 document_files = []
0031 xhtml = 0
0032 trac_url = None
0033 trac_links = [('wiki', 'wiki', 'Collaborative documentation'),
0034 ('repository', 'browser', 'Browse subversion repository'),
0035 ('roadmap', 'roadmap', 'View the project roadmap'),
0036 ('feedback!', 'newticket', 'Report an issue / request an enhancement')]
0037 mailing_list_url = None
0038 organization = None
0039 organization_name = None
0040 blog_url = None
0041 syndication_url = None
0042 template_dir = None
0043 theme = None
0044 stages = ['copy', 'docs', 'index', 'reference', 'source']
0045
0046 def __init__(self, modules=None, dest='.', force=1):
0047 self.modules = modules or []
0048 self.dest = dest
0049 self.force = force
0050
0051 def _prepare(self):
0052 from os.path import join
0053 template_base = resource_filename(__name__, 'template')
0054 templates = []
0055 templates.insert(0, join(template_base, 'base'))
0056 if self.theme:
0057 templates.insert(0, join(template_base, self.theme))
0058 if self.template_dir:
0059 templates.insert(0, self.template_dir)
0060 for t in reversed(templates):
0061 kid.path.insert(t)
0062 self.templates = templates
0063 self._template_cache = {}
0064
0065 def generate(self):
0066 self._prepare()
0067 if 'copy' in self.stages:
0068 self.copy_files()
0069 if 'docs' in self.stages:
0070 self.load_documents()
0071 self.generate_documents()
0072 if 'index' in self.stages:
0073 self.generate_index()
0074 if 'reference' in self.stages:
0075 self.generate_modules()
0076 if 'source' in self.stages:
0077 self.generate_source()
0078
0079 def get_template(self, name):
0080 Template = self._template_cache.get(name)
0081 if Template is None:
0082 pudge.log.debug('loading template: %s', name)
0083 Template = kid.load_template(name).Template
0084 self._template_cache[name] = Template
0085 import xml.parsers.expat
0086 try:
0087 return Template(generator=self,
0088 browser=self.browser)
0089 except xml.parsers.expat.ExpatError, e:
0090 raise '%s: File "%s", line %d\n\t%r' % (e, e.filename, e.lineno, e.source)
0092
0093 def expand_template(self, name, destfile, **kw):
0094 t = self.get_template(name)
0095 for k, v in kw.items():
0096 setattr(t, k, v)
0097 basedir = self._ensure_dir(path.dirname(destfile))
0098 filename = path.join(basedir, destfile + self.file_extension)
0099 pudge.log.info("generating: %s using template %s", filename, name)
0100 t.write(filename,
0101 output=(self.xhtml and 'xhtml-strict' or 'html-strict'),
0102 encoding='utf-8')
0103
0104 def load_documents(self):
0105 docs = []
0106 self.index_document = { 'title' : self.title, 'fragment' : '',
0107 'basename': '' }
0108 document_files = [(path.splitext(path.basename(f))[0], f)
0109 for f in self.document_files]
0110 if self.license:
0111 license_file = 'licenses/%s.rst' % (self.license, )
0112 document_files.append(('doc-license',
0113 resource_filename(__name__,
0114 license_file)))
0115 for name, file in document_files:
0116 pudge.log.debug("loading rst document: %s" % (file,))
0117 parts = rst.parts(file)
0118 parts['basename'] = name
0119 if name == 'index':
0120 self.index_document = parts
0121 else:
0122 docs.append(parts)
0123 self.documents = docs
0124
0125 def get_document(self, name):
0126 for doc in self.documents:
0127 if doc['basename'] == name:
0128 return doc
0129
0130 def generate_documents(self):
0131 for doc in self.documents:
0132 basename = doc['basename']
0133 self.expand_template('document.html',
0134 basename,
0135 parts=doc)
0136
0137 def generate_index(self):
0138 self.expand_template('master-index.html', 'index')
0139 self.expand_template('package-index.html', 'module-index')
0140
0141 def generate_modules(self):
0142 for m in self.browser.modules():
0143 self.generate_module(m)
0144
0145 def generate_module(self, m):
0146 self.check_module(m)
0147 filename = 'module-' + m.qualified_name()
0148 self.expand_template('module.html', filename,
0149 subject=m)
0150 filename += '-index'
0151 self.expand_template('module-index.html', filename,
0152 subject=m)
0153 self.generate_classes(m)
0154 for submodule in m.modules():
0155 self.generate_module(submodule)
0156
0157 def generate_classes(self, m):
0158 for c in m.classes():
0159 filename = 'class-' + c.qualified_name()
0160 self.expand_template('class.html', filename,
0161 subject=c)
0162
0163 def check_module(self, m):
0164
0165
0166 pass
0167
0168 def generate_source(self):
0169 for m in self.browser.modules(recursive=1):
0170 module = m.obj
0171 src_file = inspect.getsourcefile(module)
0172 if src_file is None:
0173 pudge.log.debug('skipping: %s (no source file)'
0174 % m.qualified_name())
0175 continue
0176 src_mtime = path.getmtime(src_file)
0177 dest_file = m.relative_file('.py%s' % self.file_extension)
0178 dest_file = path.join(self.dest, dest_file)
0179 if not self.force and path.exists(dest_file):
0180 if path.getmtime(dest_file) >= src_mtime:
0181 pudge.log.debug('skipping: %s (fresh)', m.qualified_name())
0182 continue
0183 pudge.log.info("colorizing: %s", dest_file)
0184 if not path.exists(path.dirname(dest_file)):
0185 os.makedirs(path.dirname(dest_file))
0186 fout = open(dest_file, 'w')
0187 colorizer.Parser(src_file, fout).format()
0188 fout.close()
0189 os.utime(dest_file, (-1, src_mtime))
0190
0191 def copy_files(self):
0192 from shutil import copy
0193 from os import listdir
0194 from os.path import join, splitext, getmtime, exists
0195 extensions = ['.css', '.png', '.txt']
0196 templates = self.templates[:]
0197 templates.reverse()
0198 files = {}
0199 self._ensure_dir()
0200 for basedir in templates:
0201 for f in listdir(basedir):
0202 root, ext = splitext(f)
0203 if ext in extensions and not root.startswith('.'):
0204 files[f] = join(basedir, f)
0205 dest, force = self.dest, self.force
0206 for basename, file in files.items():
0207 destfile = join(dest, basename)
0208 if not force and exists(destfile):
0209 if getmtime(destfile) >= getmtime(file):
0210 pudge.log.debug('skipping: %s (fresh)', file)
0211 continue
0212 pudge.log.info('copying: %s -> %s', file, destfile)
0213 copy(file, self.dest)
0214
0215 def _ensure_dir(self, *dirs):
0216 p = path.join(self.dest, *dirs)
0217 if not path.exists(p):
0218 os.makedirs(p)
0219 return p
0220
0221 def browser(self):
0222 if not hasattr(self, '_browser'):
0223 self._browser = browser.Browser(self.modules)
0224 return self._browser
0225 browser = property(browser)
0226
0227 __call__ = generate
0228
0229class Page(object):
0230
0231 def link_to(self, obj):
0232 if not obj:
0233 return
0234 if obj.ismodule():
0235 path = 'module-%s.html' % (obj.qualified_name(), )
0236 elif obj.isclass():
0237 path = 'class-%s.html' % (obj.qualified_name(), )
0238 elif obj.isroutine() or obj.isdata():
0239 if obj.parent.ismodule():
0240 path = 'module-%s.html#%s'
0241 elif obj.parent.isclass():
0242 path = 'class-%s.html#%s'
0243 path = path % (obj.parent.qualified_name(),
0244 obj.name)
0245 return path
0246
0247 def link_to_source(self, obj):
0248 (line, last_line) = obj.source_lines()
0249 if line:
0250 hash = '#%d' % line
0251 params = '?f=%d&l=%d' % (line, last_line)
0252 else:
0253 line = '???'
0254 hash = params = ''
0255 return '%s.html%s%s' % (obj.relative_file(),
0256 params, hash)
0257
0258__all__ = ['Generator']
0259
0260
0261__author__ = "Ryan Tomayko <rtomayko@gmail.com>"
0262__date__ = "$Date: 2005-07-10 22:30:09 -0400 (Sun, 10 Jul 2005) $"
0263__revision__ = "$Revision: 63 $"
0264__url__ = "$URL: svn://lesscode.org/pudge/trunk/pudge/generator.py $"
0265__copyright__ = "Copyright 2005, Ryan Tomayko"
0266__license__ = "MIT <http://www.opensource.org/licenses/mit-license.php>"