""" Code for converting an example in a repository checkout to data on the PyObjC website A sample of the website consists of a zipfile with all code for the sample, and index.html with a description of the example and furthermore HTML files with colorized source code. """ from pygments import highlight from pygments.lexers import get_lexer_for_filename from pygments.formatters import HtmlFormatter from docutils.core import publish_parts from distutils import log import zipfile import os import shutil gSkipDirectories = ('.svn', 'CVS') gZipTemplate = "PyObjCExample-%s.zip" def zipDirectory(outputname, inputdir): """ Create a zipfile containing all files in a directory, but skipping version-management turds. """ outdn = os.path.dirname(outputname) if not os.path.exists(outdn): os.makedirs(outdn) zf = zipfile.ZipFile(outputname, 'w') basename = os.path.basename(outputname)[:-4] while inputdir.endswith(os.path.sep): inputdir = inputdir[:-1] for dirpath, dirnames, filenames in os.walk(inputdir): for name in gSkipDirectories: if name in dirnames: dirnames.remove(name) # Relpath is the directory name in the zipfile, # prepend the sample name to ensure we don't extract # into the current directory. relpath = dirpath[len(inputdir)+1:] relpath = os.path.join(basename, relpath) for fn in filenames: zf.write( os.path.join(dirpath, fn), os.path.join(relpath, fn), zipfile.ZIP_DEFLATED) zf.close() def colorizeSources(inputdir, outputdir): """ Return a list of colorized files: [(relativepath, coloredName, html-style, html-body), ...] This will *not* write the actual files """ coloredFiles = [] for dirpath, dirnames, filenames in os.walk(inputdir): for name in gSkipDirectories: if name in dirnames: dirnames.remove(name) for name in dirnames: # Don't bother looking inside nib files. if name.endswith('.nib'): dirnames.remove(name) relpath = dirpath[len(inputdir)+1:] for fn in sorted(filenames): if relpath: path = os.path.join(relpath, fn) else: path = fn ext = os.path.splitext(fn)[-1] if ext not in ('.py', '.pyw', '.m', '.h'): # Skip all files that aren't clearly source code continue colorFn = 'source--%s.html' % (path.replace(os.path.sep, '-'),) fullpath = os.path.join(dirpath, fn) lexer = get_lexer_for_filename(fullpath) formatter = HtmlFormatter(linenos = False, cssclass='source') result = highlight(open(fullpath, 'r').read(), lexer, formatter) style=formatter.get_style_defs() coloredFiles.append((path, colorFn, style, result)) return coloredFiles def restToHTML(inputFile): """ Read a reStructuredText file and return the HTML representation of it """ input = open(inputFile, 'rU').read() output = publish_parts( source=input, source_path=inputFile, writer_name='html', settings_overrides=dict( input_encoding='utf-8', initial_header_level=2, )) #print output.keys() return output['html_body'] def convertSample(generator, outputdir, name, inputdir, allProjects): """ Convert an example from the source tree to part of the website Returns a small summary of the example. """ zipname = gZipTemplate % name zipDirectory( generator.localPathForSitePath(os.path.join(outputdir, zipname)), inputdir) coloredFiles = colorizeSources(inputdir, outputdir) readme = os.path.join(inputdir, "ReadMe.txt") summary = os.path.join(inputdir, "Summary.txt") if os.path.exists(readme): readme = restToHTML(readme) elif os.path.exists(summary): readme = restToHTML(summary) else: readme = "A PyObjC Example without documentation" generator.emitHTML( os.path.join(outputdir, 'index.html'), 'sample-index.html', title=name, sources=[item[:2] for item in coloredFiles], zipname=zipname, readme=readme, bottommenu=allProjects, ) for realpath, htmlpath, style, body in coloredFiles: sources=[(item[0], item[1], '') for item in coloredFiles if item[1] != htmlpath] sources.insert(0, (realpath, htmlpath, 'selected')) generator.emitHTML( os.path.join(outputdir, htmlpath), 'sample-source.html', title='%s -- %s'%(name, realpath), sources=sources, style=style, zipname=zipname, body=body, bottommenu=allProjects) # XXX: We should extract a small summary from the readme file to use # on the sample index package (and not a seperate summary file). if os.path.exists(summary): return restToHTML(summary) return "" def haveSamplesForProject(inputdir): for dirpath, dirnames, filenames in os.walk(inputdir): for dn in dirnames: if os.path.exists(os.path.join(dirpath, dn, 'setup.py')): return True return False def samplesForProject(generator, outputdir, framework, package, inputdir, allProjects=[]): inputdir = os.path.join(inputdir, 'Examples') outputdir = os.path.join(outputdir, package) if not os.path.exists(inputdir): return None samples = [] bottommenu=[] if allProjects: for title, subdir in allProjects: if title == framework: bottommenu.append((title, '')) else: bottommenu.append((title, subdir)) for dirpath, dirnames, filenames in os.walk(inputdir): for dn in dirnames: if os.path.exists(os.path.join(dirpath, dn, 'setup.py')): # Found a sample relpath = os.path.join(dirpath[len(inputdir)+1:], dn) summary = convertSample(generator, os.path.join(outputdir, relpath), dn, os.path.join(dirpath, dn), allProjects) samples.append((relpath, dn, summary)) if samples: generator.emitHTML( os.path.join(outputdir, 'index.html'), 'sample-framework-index.html', title="Examples for %s" % (framework,), samples=samples, bottommenu=bottommenu, ) return samples def generateSamples(generator, outputdir, basedir, frameworkList): log.info("Generating HTML for sample code") allSamples = [] allProjects = [] for nm in frameworkList: framework = '%s' % ( nm[len('pyobjc-framework-'):], ) if haveSamplesForProject(os.path.join(basedir, nm)): allProjects.append((framework, os.path.join(outputdir, nm, 'index.html'))) for nm in frameworkList: framework = '%s' % ( nm[len('pyobjc-framework-'):], ) log.info(" - %s" % framework) listing = samplesForProject( generator, outputdir, framework, nm, os.path.join(basedir, nm), allProjects) if listing: allSamples.append((framework, nm, listing)) if allSamples: bottommenu = [] for framework, nm, listing in allSamples: bottommenu.append((framework, os.path.join(nm, 'index.html'))) generator.emitHTML( os.path.join(outputdir, 'index.html'), 'sample-global-index.html', samplelist=allSamples, bottommenu=bottommenu)