#! /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 "
tags to replace \n
'p': use
tags to replace \n and wrap in a
...
'rbr': usrtags to replace \n\s*\n and wrap in a
...
""" if s is None: return '' elif not isinstance(s, type('')): s = str(s) s = '&'.join(s.split('&')) s = '<'.join(s.split('<')) s = '>'.join(s.split('>')) s = '"'.join(s.split('"')) if nl == 'br': s = '' + '
'.join(s.split('\n')) + '
' elif nl == 'rbr': import re s = re.sub('\n\\s+\n', '\n\n', s) s = '' + '
'.join(s.split('\n\n')) + '
' 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