#! /usr/bin/env python2.2
#
#    Syntax Hilight a python source file using CSS.
#    Copyright (C) 2002  Michael Urman
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

"""
Create an HTML summary of a python module.

Unlike pydoc's output, this is "nice" CSS HTML4 code.
"""

__version__ = '0.2'
__date__ = '2003/05/03'
__author__ = 'Michael Urman (mu on irc.freenode.net)'

from __future__ import generators
import sys, types, os

class Object:
    """Base class for the Object Hierarchy"""

    def __init__(self, obj, name=None):
        """__init_(self, obj,name=None) -> Object

        Initialize an Object with optional parameter name"""

        self._obj = obj
        self._name = name
        self._dir = dir(obj)
        self._dir.sort()

    def doc(self):
        """doc(self) -> docstring

        Return the object's docstring, or None if not available"""

        if hasattr(self._obj, '__doc__'):
            if type(self._obj) not in (type(4), type(0.0), type(''),
                                        type(()), type([]), type({})):
                doc = getattr(self._obj, '__doc__')
                return doc
                if doc is not None:
                    return doc.split('\n')[0]

    def name(self):
        """name(self) -> str

        Return the object's name, either as specified, or stored"""

        if self._name is not None:
            return self._name
        elif hasattr(self._obj, '__name__'):
            return getattr(self._obj, '__name__')
        else:
            return None

    def __str__(self):
        """__str__(self) -> str

        Represent an object with its name (by default)"""

        if self._name is not None:
            return self._name
        else:
            try: 
                return self._obj.__name__
            except AttributeError:
                return '<%s>' % self.__class__

class Var(Object):
    "Represent variables"
    def val(self):
        return repr(self._obj)

class Property(Object):
    "Represent properties"
    def val(self):
        o = self._obj
        fget = o.fget and MethodRef(o.fget)
        fset = o.fset and MethodRef(o.fset)
        fdel = o.fdel and MethodRef(o.fdel)
        doc = o.__doc__
        if doc is not None:
            return 'property(fget=%s, fset=%s, fdel=%s, doc=%s)' % (fget, fset, fdel, doc)
        elif fdel is not None:
            return 'property(fget=%s, fset=%s, fdel=%s)' % (fget, fset, fdel)
        elif fset is not None:
            return 'property(fget=%s, fset=%s)' % (fget, fset)
        elif fget is not None:
            return 'property(fget=%s)' % (fget)
        else:
            return 'property()'

class Arg(Var):
    "Represent arguments to a function"
    pass

class DArg(Arg):
    "Represent arguments with a default value"

    def __init__(self, obj, name=None, default=None):
        """__init__(self, obj, name=None, default=None) -> DArg

        Initialize a DArg with default value"""

        Arg.__init__(self, obj, name)
        self._default = default

    def __str__(self):
        return self.name() + '=' + str(self._default)

    def default(self):
        """default(self) -> str

        Return a stringified version of the default value"""

        return str(self._default)

class VArg(Arg):
    "Represent an argument grabbing list"
    def __str__(self):
        return '*' + self.name()

class KWArg(Arg):
    "Represent an argument grabbing dictionary"
    def __str__(self):
        return '**' + self.name()

class Function(Object):
    "Represent a function"

    def getcode(self): 
        return self._obj.func_code

    def getdefaults(self):
        return self._obj.func_defaults

    def args(self):
        """args(self) -> list of Arg (and Arg derived) objects

        Return a list generator of function arguments"""

        code = self.getcode()
        argnames = code.co_varnames
        nargs = code.co_argcount
        flags = code.co_flags
        defaults = self.getdefaults()
        if defaults is None:
            ndef = 0
        else:
            ndef = len(defaults)
        for arg, ind in zip(argnames[:nargs], range(nargs)):
            if ind >= nargs - ndef:
                yield DArg(self._obj, arg, defaults[ndef-nargs+ind])
            else:
                yield Arg(self._obj, arg)
        if flags & 4: # has * argument
            yield VArg(self._obj, argnames[nargs])
        if flags & 8: # has ** argument
            yield KWArg(self._obj, argnames[nargs+((flags&4)>>2)])

    def __str__(self):
        return '%s(%s)' % (self.name(),
            ', '.join([str(arg) for arg in self.args()]))

class Method(Function):
    "Represent a method"
    def __init__(self, obj, name=None, class_=None):
        """__init__(self, obj, name=None, class_=None) -> Method

        Initialize an Method with its class"""

        Function.__init__(self, obj, name)
        self._class = class_
    def getcode(self):
        return self._obj.im_func.func_code
    def getdefaults(self):
        return self._obj.im_func.func_defaults

    def cls(self):
        """cls(self) -> Clas

        Return this method's Class"""

        return self._class

class InheritedMethod(Method):
    "Represent a method inherited from a parent class"
    def __init__(self, obj, name=None, class_=None, from_=None):
        """__init__(self, obj, name=None, class_=None, from_=None) -> InheritedMethod

        Initialize an InheritedMethod with class from which it inherits"""

        Method.__init__(self, obj, name, class_)
        self._from = from_

    def inherited(self):
        """inherited(self) -> str

        Return the name of the class from which the method is inherited"""

        return self._from.name()

class MethodRef(Method):
    "Represent some sort of method reference, such as used in a property"

    def __str__(self):
        if hasattr(self._obj, '__name__'):
            return self._obj.__name__
        elif isinstance(self._obj, classmethod):
            # this hack is really really gross.  but i can't get the function
            # name without doing something wacky like this...
            class tmp(object):
                cm = self._obj
            cm = tmp.cm
            return 'classmethod(%s)' % cm.__name__
        elif isinstance(self._obj, staticmethod):
            class tmp(object):
                sm = self._obj
            sm = tmp.sm
            return 'staticmethod(%s)' % sm.__name__
        else:
            return '[...]'

class Class(Object):
    "Represent a class"

    def vars(self):
        """vars(self) -> list of Var objects

        Return a list generator of class variables"""

        for name in self._dir:
            if not name.startswith('__'):
                var = getattr(self._obj, name)
                if not isinstance(var, (types.UnboundMethodType, property)):
                    yield Var(var, name)

    def properties(self):
        """properties(self) -> list of property objects

        Return a list generator of properties of the class"""

        for name in self._dir:
            if not name.startswith('__'):
                var = getattr(self._obj, name)
                if isinstance(var, property):
                    yield Property(var, name)

    def methods(self):
        """methods(self) -> list of Method objects

        Return a list generator of methods in the class"""

        for name in self._dir:
            #if not name.startswith('__'):
                meth = getattr(self._obj, name)
                if isinstance(meth, types.UnboundMethodType):
                    # figure out if inherited
                    fc = meth.im_func.func_code
                    found = 0
                    curbase = self._obj
                    while not found:
                        for base in curbase.__bases__:
                            try:
                                if getattr(base, name).im_func.func_code == fc:
                                    curbase = base
                                    break
                            except AttributeError:
                                pass
                        else:
                            found = 1
                    if curbase == self._obj:
                        yield Method(meth, class_=self.name())
                    else:
                        yield InheritedMethod(meth, class_=self.name(),
                                                from_=Class(curbase))

    def const(self):
        """const(self) -> Method

        Return the Method corresponding to __init__()"""

        if '__init__' in self._dir:
            meth = getattr(self._obj, '__init__')
            yield Method(meth, self.name(), class_=self.name())

    def bases(self):
        """bases(self) -> list of Class objects

        Return a list generator of Class self's base classes"""

        for base in self._obj.__bases__:
            yield Class(base)
        #return [Class(b) for b in self._obj.__bases__]

    def subclasses(self):
        import sys
        module = Module(sys.modules[self._obj.__module__])
        for cls in module.classes():
            if self.name() in [c.name() for c in cls.bases()]:
                yield cls

class Module(Object):
    "Represent a module"

    def modules(self):
        """modules(self) -> list of Module objects

        Return a list of modules defined (imported into) the module"""

        for name in self._dir:
            if not name.startswith('__'):
                mod = getattr(self._obj, name)
                if isinstance(mod, types.ModuleType):
                    yield Module(mod)

    def classes(self):
        """classes(self) -> list of Class objects

        Return a list generator of all classes in the module"""

        for name in self._dir:
            cls = getattr(self._obj, name)
            if isinstance(cls, (types.ClassType, types.TypeType)):
                yield Class(cls)

    def functions(self):
        """functions(self) -> list of Function objects

        Return a list generator of all module level functions"""

        for name in self._dir:
            func = getattr(self._obj, name)
            if isinstance(func, types.FunctionType):
                yield Function(func)

    def vars(self):
        """vars(self) -> list of Var objects

        Return a list generator of module level variables"""

        for name in self._dir:
            if not name.startswith('__'):
                var = getattr(self._obj, name)
                if not (isinstance(var, (types.UnboundMethodType, types.ModuleType, types.FunctionType, types.ClassType, types.TypeType))):
                    yield Var(var, name)

    def baseclasses(self):
        """baseclasses(self) -> list of Class objects
        
        Return a list generator of base classes; classes which have no parent
        classes in the module"""

        for cls in self.classes():
            if genlen(cls.bases()) == 0:
                yield cls

    def author(self):
        """Return module.__author__ or None"""
        if '__author__' in self._dir:
            return getattr(self._obj, '__author__')

    def date(self):
        """Return module.__date__ or None"""
        if '__date__' in self._dir:
            return getattr(self._obj, '__date__')

    def version(self):
        """Return module.__version__ or None"""
        if '__version__' in self._dir:
            return getattr(self._obj, '__version__')

class Attribute:
    "Handle CSS attributes somewhat python style"
    def __init__(self, sel, bg=None, fg=None, **kw):
        self.sel = sel
        for k, v in kw.items()[:]:
            if k.find('_') >= 0:
                newk = '-'.join(k.split('_'))
                del kw[k]
                kw[newk] = v
        if bg: kw['background-color'] = bg
        if fg: kw['color'] = fg
        self.styledict = kw

    def __str__(self):
        return ' '.join([self.sel, '{']+[ '%s: %s;'%(k,v) for k,v in self.styledict.items()]+['}']) + '\n'

    def __repr__(self):
        return "<Attribute sel='%s'>" % self.sel

attributes = [
Attribute('body', '#f8f8f8', '#000'),
Attribute('a', text_decoration='None'),
Attribute('a:hover', text_decoration='underline'),
Attribute('.doc', font_family='monospace', padding_left='20px'),
Attribute('.list', font_size='x-small', padding_left='20px', word_spacing='1em'),
Attribute('.head > .info', font_size='small'),
#Attribute('.super'),
#Attribute('.inherit'),

Attribute('.module > .head', 'green', 'white',
            padding='5px', padding_top='25px', margin_bottom='5px',
            font_weight='bold', font_size='xx-large'),
Attribute('.module > .doc', white_space='pre'),
Attribute('.module > .info', padding_left='20px', margin_top='10px'),
Attribute('.usemodules',
            border_left='solid 30px blue',
            border_top='solid 30px blue',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.usemodules > .head', 'blue', 'white',
            font_weight='bold', font_size='x-large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-15px', margin_bottom='5px'),
Attribute('.classes',
            border_left='solid 30px red',
            border_top='solid 30px red',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.classtree', margin_left='10px'),
Attribute('.subclasstree', margin_left='20px'),
Attribute('a.tree', font_weight='bold', display='block'),
Attribute('.classtree > .tree:before', content='"* "'),
Attribute('.subclasstree > .tree:before', content='"+ "'),
Attribute('.classes > .head', 'red', 'white',
            font_weight='bold', font_size='x-large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-15px', margin_bottom='5px'),
Attribute('.class',
            border_left='solid 20px pink',
            border_top='solid 20px pink',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.class > .head', 'pink', 'black',
            font_weight='bold', font_size='large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-10px', margin_bottom='5px'),
Attribute('.class > .doc', bg='#bbf',
            padding='3px 20px', font_weight='bold'),
Attribute('.vars',
            border_left='solid 10px #ff8',
            border_top='solid 10px #ff8',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.vars > .head', '#ff8',
            font_weight='bold', font_size='large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-5px', margin_bottom='5px'),
#Attribute('.varinfo'),
Attribute('.varinfo > .var', font_weight='bold'),
Attribute('.varinfo > .val', font_family='monospace'),
Attribute('.methods',
            border_left='solid 10px #ff8',
            border_top='solid 10px #ff8',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.methods > .head', '#ff8',
            font_weight='bold', font_size='large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-5px', margin_bottom='5px'),
Attribute('.method, .function', margin_top='5px', margin_bottom='5px',
            border_style='solid', border_color='black',
            border_width='2px 1px 1px 2px', padding='3px'),
#Attribute('.method > .head', margin_bottom='3px'),
Attribute('.method > .head + .doc, .function > .head + .doc',
            margin_top='3px'),
Attribute('.method > .doc, .function > .doc', fg='maroon',
            border_style='solid', border_color='grey',
            border_width='1px 0px', padding='2px 2px', margin='0px 20px'),
Attribute('.funcname', font_weight='bold'),
Attribute('.argname', font_family='monospace'),
Attribute('.argdef', fg='#888', font_style='italic'),
Attribute('.functions',
            border_left='solid 30px brown',
            border_top='solid 30px brown',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.functions > .head', 'brown', 'white',
            font_weight='bold', font_size='x-large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-15px', margin_bottom='5px'),
#Attribute('.function > .head'),
Attribute('.data',
            border_left='solid 30px purple',
            border_top='solid 30px purple',
            padding_left='5px',
            margin_top='5px', margin_bottom='5px'),
Attribute('.data > .head', 'purple', 'white',
            font_weight='bold', font_size='x-large',
            padding_top='0px', padding_bottom='5px',
            margin_left='-15px', margin_bottom='5px'),
Attribute('.datum', margin='5px 5px 5px 20px', padding='3px'),
#Attribute('.datum:first-line', text_indent='-15px'),
Attribute('.datum > .var', font_weight='bold', margin_left='-20px'),
Attribute('.datum > .val', font_family='monospace'),
]

def writecss(dst):
    for attr in attributes:
        dst.write(str(attr))

def esc(s, nl='br'):
    r"""escape a string for html safety.  nl can be one of the following:

        'br': use <br /> tags to replace \n

        'p': use </p><p> tags to replace \n and wrap in a <p>...</p>

        'rbr': usr <br /><br /> tags to replace \n\s*\n

        'rp: use </p><p> tags to replace \n\s*\n and wrap in a <p>...</p>
    """
    if s is None:
        return ''
    elif not isinstance(s, type('')):
        s = str(s)
    s = '&amp;'.join(s.split('&'))
    s = '&lt;'.join(s.split('<'))
    s = '&gt;'.join(s.split('>'))
    s = '&quot;'.join(s.split('&quot'))
    if nl == 'br':
        s = '<br>'.join(s.split('\n'))
    elif nl == 'p':
        s = '<p>' + '</p><p>'.join(s.split('\n')) + '</p>'
    elif nl == 'rbr':
        import re
        s = re.sub('\n\\s+\n', '\n\n', s)
        s = '<br><br>'.join(s.split('\n\n'))
    elif nl == 'rp':
        import re
        s = re.sub('\\s+\n', '\n', s)
        s = '<p>' + '</p><p>'.join(s.split('\n\n')) + '</p>'
    return s

indentstr = '  '
def writefuncs(out, title, indent=0, css=('functions', 'function'), *funcs):
    count = 0
    allfuncs = []
    for gen in funcs:
        for func in gen:
            allfuncs.append(func)
    if len(allfuncs) == 0:
        return

    strdict = {
        'i0': indentstr * indent,
        'i1': indentstr * (indent+1),
        'i2': indentstr * (indent+2),
        'i3': indentstr * (indent+3),
        'functions': css[0],
        'function': css[1],
        'title': title,
    }

    # \functions
    out('\n%(i0)s<div class="%(functions)s">' % strdict)
    out('\n%(i1)s<div class="head">%(title)s</div>' % strdict)

    if isinstance(allfuncs[0], Method):
        out('\n%(i1)s<div class="list">' % strdict)
        for func in allfuncs:
            strdict['cls'] = esc(func.cls())
            strdict['name'] = esc(func.name())
            out('\n%(i2)s<a href="#method-%(cls)s-%(name)s">%(name)s</a>' % strdict)
        out('\n%(i1)s</div>' % strdict)
    elif isinstance(allfuncs[0], Function):
        out('\n%(i1)s<div class="list">' % strdict)
        for func in allfuncs:
            strdict['name'] = esc(func.name())
            out('\n%(i1)s<a href="#function-%(name)s">%(name)s</a>' % strdict)
        out('\n%(i1)s</div>' % strdict)


    for func in allfuncs:
        strdict['name'] = esc(func.name())
        # \function
        out('\n%(i1)s<div class="%(function)s">' % strdict)
        if isinstance(func, Method):
            out('\n%(i2)s<a name="method-%(cls)s-%(name)s"></a>' % strdict)
        elif isinstance(func, Function):
            out('\n%(i2)s<a name="function-%(name)s"></a>' % strdict)
        out('\n%(i2)s<div class="head">' % strdict)
        out('<span class="funcname">%(name)s</span>(' % strdict)
        sep = ''
        for arg in func.args():
            out(sep)
            if isinstance(arg, VArg):
                out('<span class="argdef">*</span>')
            elif isinstance(arg, KWArg):
                out('<span class="argdef">**</span>')
            out('<span class="argname">%s</span>' % esc(arg.name()))
            sep = ', '
            if isinstance(arg, DArg):
                out('<span class="argdef">=%s</span>' % esc(arg.default()))
        if isinstance(func, InheritedMethod):
            super = { 'class': esc(func.inherited()),
                      'method': esc(func.name()) }
            out(') from <a class="inherit" href=')
            out('"#method-%(class)s-%(method)s">%(class)s</a></div>' % super)
        else:
            out(')</div>')
            if func.doc():
                strdict['doc'] = esc(func.doc(), nl='rbr')
                out('\n%(i2)s<div class="doc">%(doc)s</div>' % strdict)
        # /function
        out('\n%(i1)s</div>' % strdict)
    # /functions
    out('\n%(i0)s</div>' % strdict)

def writevars(out, title, indent=0, css=('data', 'datum'), *vars):
    allvars = []
    for gen in vars:
        for var in gen:
            allvars.append(var)
    if len(allvars) == 0:
        return
    strdict = {
        'i0': indentstr * indent,
        'i1': indentstr * (indent+1),
        'i2': indentstr * (indent+2),
        'i3': indentstr * (indent+3),
        'vars': css[0],
        'var': css[1],
        'title': title,
    }

    # \vars
    out('\n%(i0)s<div class="%(vars)s">' % strdict)
    out('\n%(i1)s<div class="head">%(title)s</div>' % strdict)
    for var in allvars:
        out('\n%(i2)s<div class="%(var)s">' % strdict)
        out('<span class="var">%s</span>' % esc(var.name()))
        out(' = <span class="val">%s</span>' % esc(var.val()))
        if var.doc():
            out(' <span class=".doc">%s</span>' % esc(var.doc()))
        out('</div>')

    #if count == 0:
    #    out('\n    <div class="datum">None</div>')

    # /vars
    out('\n</div>')

def writehtml(module, dst, css, title='Syntax Hilight'):
    out = dst.write

    out('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"')
    out('\n    "http://www.w3.org/TR/html4/strict.dtd">')
    out('\n<html>')
    out('\n  <head>''')
    out('\n    <title>%s</title>' % title)
    if css:
        out('\n    <link rel="stylesheet" href="%s" />' % css)
    else:
        out('\n    <style type="text/css"><!--')
        writecss(dst)
        out('\n    --></style>')
    out('\n  </head>')
    out('\n  <body>')

    # Module name
    out('\n<div class="module">')
    out('\n  <div class="head">%s <span class="info">%s %s</span></div>' %
        (esc(module.name()), esc(module.version()), esc(module.date())))

    # Module __doc__
    if module.doc():
        out('\n  <div class="doc">' + esc(module.doc()) + '</div>')
    authors = module.author()
    if isinstance(authors, type('')):
        out('\n  <div class="info">Written by %s</div>' % esc(authors))
    elif isinstance(authors, type(())) or isinstance(authors, type([])):
        if len(authors) == 1:
            out('\n  <div class="author">Author: %s</div>' % esc(authors[0]))
        else:
            out('\n  <div class="author">Authors:')
            for author in authors:
                out(' %s' % esc(author))
            out('</div>')
    out('\n</div>')

    # Used modules
    if genlen(module.modules()):
        out('\n<div class="usemodules">')
        out('\n  <div class="head">Uses Modules</div>')
        for mod in module.modules():
            out('\n  %s' % esc(mod.name()))
        out('\n</div>')

    if genlen(module.classes()):
        # Classes
        out('\n<div class="classes">')
        out('\n  <div class="head">Classes</div>')

        # list them
        #out('\n  <div class="list">')
        #for cls in module.classes():
        #    name = esc(cls.name())
        #    out('\n    <a href="#class-%s">%s</a>' % (name, name))
        #out('\n  </div>')

        # show a tree
        queue = []
        queue.extend(module.baseclasses())
        out('\n  <div class="classtree">')
        while len(queue):
            cur = queue.pop(0)
            if cur == 'pop':
                out('\n    </div>')
            else:
                name = esc(cur.name())
                out('\n      <a class="tree" href="#class-%s">%s</a>' % (name,name))
                subs = []
                subs.extend(cur.subclasses())
                if len(subs):
                    out('\n    <div class="subclasstree">')
                    subs.append('pop')
                    subs.extend(queue)
                    queue = subs
        out('\n  </div>')


        # display actual class info
        for cls in module.classes():
            name = esc(cls.name())
            bases = ', '.join(['<a class="super" href="#class-%s">%s</a>' %
                            (esc(b.name()), esc(b.name())) for b in cls.bases()])
            if len(bases): bases = '(%s)' % bases
            out('\n  <div class="class"><a name="class-%s"></a>' % name)
            out('\n    <div class="head">class ')
            out('<span class="name">%s</span>%s</div>' % (name, bases))
            if cls.doc():
                out('\n    <div class="doc">%s</div>' % esc(cls.doc(), nl='rbr'))

            # Class Variables
            writevars(out, 'Variables', 2, ('vars', 'varinfo'),
                        cls.vars())

            writevars(out, 'Properties', 2, ('vars', 'varinfo'),
                        cls.properties())

            # Methods
            writefuncs(out, 'Methods', 2, ('methods','method'), cls.methods())

            # /class
            out('\n  </div>')
        # /classes
        out('''\n</div>''')
    
    if genlen(module.functions()):
        writefuncs(out, 'Functions', 0, ('functions','function'),
                module.functions())

    if genlen(module.vars()):
        writevars(out, 'Global Data', 0, ('data', 'datum'),
                module.vars())

    out('\n</div>')
    out('\n  </body>')
    out('\n</html>\n')
    dst.flush()

def genlen(gen):
    """genlen(gen) -> bool

    Return whether generator has any elements by testing for StopIteration"""
    try:
        x = gen.next()
    except StopIteration:
        return 0
    return 1

def writetext(module, dst):
    print 'MODULE', module.name()
    doc = module.doc()
    if doc: print '+DOC', doc
    print ' USEMOD',
    for mod in module.modules():
        print mod.name(),
    print
    for cls in module.classes():
        print ' CLASS', cls.name()
        doc = cls.doc()
        if doc: print ' +DOC', doc
        for var in cls.vars():
            print '  VAR', var.name(), '=', var.val()
        for const in cls.const():
            print '  CONST', const.name()
            doc = const.doc()
            if doc: print '  +DOC', doc
        for meth in cls.methods():
            print '  METH', meth
            doc = meth.doc()
            if doc: print '  +DOC', doc
    for func in module.functions():
        print ' FUNC', func
        doc = func.doc()
        if doc: print ' +DOC', doc
    for var in module.vars():
        print ' VAR', var.name(), '=', var.val()

def main():
    sys.path.insert(0, os.getcwd())
    if len(sys.argv) == 2:
        src = __import__(sys.argv[1])
        dst = sys.stdout
        css = None
    elif len(sys.argv) == 3:
        src = __import__(sys.argv[1])
        if sys.argv[2] == '-':
            dst = sys.stdout
            css = None
        else:
            css = os.path.join(os.path.split(sys.argv[2])[0], 'python.css')
            dst = open(css, 'w', 1)
            writecss(dst)
            dst.close()
            css = 'python.css'
            dst = open(sys.argv[2], 'w', 1)

    module = Module(src)
    writehtml(module, dst, css, 'Module %s Documentation' % os.path.basename(sys.argv[1]))

if __name__ == '__main__': main()