py2html — a Python source code pretty printer

py2html

#!/usr/bin/python

"""
An HTML pretty printer for Python source code using the GiPSy scanner.

Release 1.1

Copyright 2013 Paul Griffiths
Email: mail@paulgriffiths.net

Distributed under the terms of the GNU General Public License.
http://www.gnu.org/licenses/
"""

import sys
from optparse import OptionParser
from gipsy import Py2HTMLParser


def output_html_header(outfile, title, lang):

    """
    Outputs an HTML header.

    Arguments:
    outfile -- a file-like object to output to
    title -- a string for the <title> and <h1> tags
    lang -- "xhtml" or "html5"
    """

    pre_title_xhtml = r"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">

<head>
  <title>"""

    pre_title_html5 = r"""<!DOCTYPE HTML>

<html>

<head>
  <title>"""

    post_title = r"""</title>
  <link rel="stylesheet" href="/css/code2html.css" type="text/css" media="screen" />
  <link rel="stylesheet" href="/css/py2html.css" type="text/css" media="screen" />
</head>

<body>

<h1>"""

    post_heading = r"""</h1>

"""

    if lang == "xhtml":
        pre_title = pre_title_xhtml
    else:
        pre_title = pre_title_html5

    content = pre_title + title + post_title + title + post_heading
    outfile.write(content)


def output_html_footer(outfile):

    """
    Outputs an HTML footer.

    Arguments:
    outfile -- a file-like object to output to
    """

    outfile.write("</body>\n\n</html>\n\n")


def main():

    """
    py2html main() function.
    """

    # Set up OptionParser and get command line arguments

    usage = "%prog [OPTIONS]... [FILES]..."
    version = "%prog 1.0"
    parser = OptionParser(usage=usage, version=version)

    parser.add_option("-o", "--outfile", dest="outfile",
                      help="write output to FILE (default: STDOUT)",
                      metavar="FILE")
    parser.add_option("-t", "--tabs", dest="tabs", default="4",
                      help="replace tabs with N spaces (default: 4)",
                      metavar="N")
    parser.add_option("--title", dest="title", default="py2html Output",
                      help="set main page heading " +
                           "(default: \"py2html Output\")")
    parser.add_option("-l", "--lang", dest="lang", default="xhtml",
                      help="output in \"xhtml\" or \"html5\" " +
                           "(default: \"xhtml\")",
                      metavar="LANG")

    (options, args) = parser.parse_args()

    # Convert tabs option to integer

    try:
        tabs = int(options.tabs)
    except ValueError:
        parser.error("you must specify an integer for tabs.")

    # Make sure at least one input file was specified

    if len(args) == 0:
        parser.error("you must specify at least one input file.")

    # Get desired language

    accepted_langs = ["html5", "xhtml"]
    if options.lang not in accepted_langs:
        parser.error("invalid language specified: %s" % options.lang)

    # Try to open all input files in advance, so we can
    # quit with an error message before we've started
    # outputting if one is invalid

    infiles = []
    for f_name in args:
        try:
            infiles.append(open(f_name, "r"))
        except IOError:
            for infile in infiles:
                infile.close()
            parser.error("couldn't open file '%s' for reading.\n" % f_name)

    # Check for, and try to open, output file

    if options.outfile:
        try:
            outfile = open(options.outfile, "w")
        except IOError:
            for infile in infiles:
                infile.close()
            parser.error("couldn't open file '%s' for writing.\n" %
                         options.outfile)
    else:
        outfile = sys.stdout

    # Create Py2HTML parser

    pyparser = Py2HTMLParser()

    # Output HTML

    output_html_header(outfile, options.title, options.lang)

    for infile, f_name in zip(infiles, args):
        pyparser.tokenize(infile.read(), tabs)

        outfile.write('<div class="sourcefile">\n')
        outfile.write('<h2>%s</h2>\n\n<pre>\n' % f_name)
        outfile.write(pyparser.read(decorated=True, html=True))
        outfile.write('</pre>\n</div>\n\n')

        infile.close()

    output_html_footer(outfile)

    outfile.close()

# Entry point to main() function

if __name__ == "__main__":
    main()