#!/usr/bin/env python
# -*- Mode: Python; py-indent-offset: 4 -*-
import sys, os, string, re, getopt
import defsparser
import definitions
import override
import docextract
class Node:
def __init__(self, name, interfaces=[]):
self.name = name
self.interfaces = interfaces
self.subclasses = []
def add_child(self, node):
self.subclasses.append(node)
def build_object_tree(parser):
# reorder objects so that parent classes come first ...
objects = parser.objects[:]
pos = 0
while pos < len(objects):
parent = objects[pos].parent
for i in range(pos+1, len(objects)):
if objects[i].c_name == parent:
objects.insert(i+1, objects[pos])
del objects[pos]
break
else:
pos = pos + 1
root = Node(None)
nodes = { None: root }
for obj_def in objects:
parent_node = nodes[obj_def.parent]
node = Node(obj_def.c_name, obj_def.implements)
parent_node.add_child(node)
nodes[node.name] = node
# TODO: Add mini-object
if parser.interfaces:
interfaces = Node('gobject.GInterface')
root.add_child(interfaces)
nodes[interfaces.name] = interfaces
for obj_def in parser.interfaces:
node = Node(obj_def.c_name)
interfaces.add_child(node)
nodes[node.name] = node
if parser.boxes:
boxed = Node('gobject.GBoxed')
root.add_child(boxed)
nodes[boxed.name] = boxed
for obj_def in parser.boxes:
node = Node(obj_def.c_name)
boxed.add_child(node)
nodes[node.name] = node
if parser.pointers:
pointers = Node('gobject.GPointer')
root.add_child(pointers)
nodes[pointers.name] = pointers
for obj_def in parser.pointers:
node = Node(obj_def.c_name)
pointers.add_child(node)
nodes[node.name] = node
return root
class DocWriter:
def __init__(self):
# parse the defs file
self.parser = defsparser.DefsParser(())
self.overrides = override.Overrides()
self.classmap = {}
self.docs = {}
def add_sourcedirs(self, source_dirs):
self.docs = docextract.extract(source_dirs, self.docs)
def add_tmpldirs(self, tmpl_dirs):
self.docs = docextract.extract_tmpl(tmpl_dirs, self.docs)
def add_docs(self, defs_file, overrides_file, module_name):
'''parse information about a given defs file'''
self.parser.filename = defs_file
self.parser.startParsing(defs_file)
if overrides_file:
self.overrides.handle_file(overrides_file)
for obj in self.parser.objects:
if not self.classmap.has_key(obj.c_name):
self.classmap[obj.c_name] = '%s.%s' % (module_name, obj.name)
for obj in self.parser.interfaces:
if not self.classmap.has_key(obj.c_name):
self.classmap[obj.c_name] = '%s.%s' % (module_name, obj.name)
for obj in self.parser.boxes:
if not self.classmap.has_key(obj.c_name):
self.classmap[obj.c_name] = '%s.%s' % (module_name, obj.name)
for obj in self.parser.pointers:
if not self.classmap.has_key(obj.c_name):
self.classmap[obj.c_name] = '%s.%s' % (module_name, obj.name)
def pyname(self, name):
return self.classmap.get(name, name)
def __compare(self, obja, objb):
return cmp(self.pyname(obja.c_name), self.pyname(objb.c_name))
def output_docs(self, output_prefix):
files = []
# class hierarchy
hierarchy = build_object_tree(self.parser)
filename = self.create_filename('hierarchy', output_prefix)
fp = open(filename, 'w')
self.write_full_hierarchy(hierarchy, fp)
fp.close()
obj_defs = self.parser.objects + self.parser.interfaces + \
self.parser.boxes + self.parser.pointers
obj_defs.sort(self.__compare)
for obj_def in obj_defs:
filename = self.create_filename(obj_def.c_name, output_prefix)
fp = open(filename, 'w')
if isinstance(obj_def, definitions.ObjectDef):
self.output_object_docs(obj_def, fp)
elif isinstance(obj_def, definitions.InterfaceDef):
self.output_interface_docs(obj_def, fp)
elif isinstance(obj_def, definitions.BoxedDef):
self.output_boxed_docs(obj_def, fp)
elif isinstance(obj_def, definitions.PointerDef):
self.output_boxed_docs(obj_def, fp)
fp.close()
files.append((os.path.basename(filename), obj_def))
if files:
filename = self.create_toc_filename(output_prefix)
fp = open(filename, 'w')
self.output_toc(files, fp)
fp.close()
def output_object_docs(self, obj_def, fp=sys.stdout):
self.write_class_header(obj_def.c_name, fp)
self.write_heading('Synopsis', fp)
self.write_synopsis(obj_def, fp)
self.close_section(fp)
# construct the inheritence hierarchy ...
ancestry = [ (obj_def.c_name, obj_def.implements) ]
try:
parent = obj_def.parent
while parent != None:
if parent == 'GObject':
ancestry.append(('GObject', []))
parent = None
else:
parent_def = self.parser.find_object(parent)
ancestry.append((parent_def.c_name, parent_def.implements))
parent = parent_def.parent
except ValueError:
pass
ancestry.reverse()
self.write_heading('Ancestry', fp)
self.write_hierarchy(obj_def.c_name, ancestry, fp)
self.close_section(fp)
constructor = self.parser.find_constructor(obj_def, self.overrides)
if constructor:
self.write_heading('Constructor', fp)
self.write_constructor(constructor,
self.docs.get(constructor.c_name, None),
fp)
self.close_section(fp)
methods = self.parser.find_methods(obj_def)
methods = filter(lambda meth, self=self:
not self.overrides.is_ignored(meth.c_name), methods)
if methods:
self.write_heading('Methods', fp)
for method in methods:
self.write_method(method, self.docs.get(method.c_name, None), fp)
self.close_section(fp)
self.write_class_footer(obj_def.c_name, fp)
def output_interface_docs(self, int_def, fp=sys.stdout):
self.write_class_header(int_def.c_name, fp)
self.write_heading('Synopsis', fp)
self.write_synopsis(int_def, fp)
self.close_section(fp)
methods = self.parser.find_methods(int_def)
methods = filter(lambda meth, self=self:
not self.overrides.is_ignored(meth.c_name), methods)
if methods:
self.write_heading('Methods', fp)
for method in methods:
self.write_method(method, self.docs.get(method.c_name, None), fp)
self.close_section(fp)
self.write_class_footer(int_def.c_name, fp)
def output_boxed_docs(self, box_def, fp=sys.stdout):
self.write_class_header(box_def.c_name, fp)
self.write_heading('Synopsis', fp)
self.write_synopsis(box_def, fp)
self.close_section(fp)
constructor = self.parser.find_constructor(box_def, self.overrides)
if constructor:
self.write_heading('Constructor', fp)
self.write_constructor(constructor,
self.docs.get(constructor.c_name, None),
fp)
self.close_section(fp)
methods = self.parser.find_methods(box_def)
methods = filter(lambda meth, self=self:
not self.overrides.is_ignored(meth.c_name), methods)
if methods:
self.write_heading('Methods', fp)
for method in methods:
self.write_method(method, self.docs.get(method.c_name, None), fp)
self.close_section(fp)
self.write_class_footer(box_def.c_name, fp)
def output_toc(self, files, fp=sys.stdout):
fp.write('TOC\n\n')
for filename, obj_def in files:
fp.write(obj_def.c_name + ' - ' + filename + '\n')
# override the following to create a more complex output format
def create_filename(self, obj_name, output_prefix):
'''Create output filename for this particular object'''
return output_prefix + '-' + string.lower(obj_name) + '.txt'
def create_toc_filename(self, output_prefix):
return self.create_filename(self, 'docs', output_prefix)
def write_full_hierarchy(self, hierarchy, fp):
def handle_node(node, fp, indent=''):
for child in node.subclasses:
fp.write(indent + node.name)
if node.interfaces:
fp.write(' (implements ')
fp.write(string.join(node.interfaces, ', '))
fp.write(')\n')
else:
fp.write('\n')
handle_node(child, fp, indent + ' ')
handle_node(hierarchy, fp)
# these need to handle default args ...
def create_constructor_prototype(self, func_def):
return func_def.is_constructor_of + '(' + \
string.join(map(lambda x: x[1], func_def.params), ', ') + \
')'
def create_function_prototype(self, func_def):
return func_def.name + '(' + \
string.join(map(lambda x: x[1], func_def.params), ', ') + \
')'
def create_method_prototype(self, meth_def):
return meth_def.of_object + '.' + \
meth_def.name + '(' + \
string.join(map(lambda x: x[1], meth_def.params), ', ') + \
')'
def write_class_header(self, obj_name, fp):
fp.write('Class %s\n' % obj_name)
fp.write('======%s\n\n' % ('=' * len(obj_name)))
def write_class_footer(self, obj_name, fp):
pass
def write_heading(self, text, fp):
fp.write('\n' + text + '\n' + ('-' * len(text)) + '\n')
def close_section(self, fp):
pass
def write_synopsis(self, obj_def, fp):
fp.write('class %s' % obj_def.c_name)
if isinstance(obj_def, definitions.ObjectDef):
bases = []
if obj_def.parent: bases.append(obj_def.parent)
bases = bases = obj_def.implements
if bases:
fp.write('(%s)' % string.join(bases, ', '))
fp.write(':\n')
constructor = self.parser.find_constructor(obj_def, self.overrides)
if constructor:
prototype = self.create_constructor_prototype(constructor)
fp.write(' def %s\n' % prototype)
methods = self.parser.find_methods(obj_def)
methods = filter(lambda meth, self=self:
not self.overrides.is_ignored(meth.c_name), methods)
for meth in methods:
prototype = self.create_method_prototype(meth)
fp.write(' def %s\n' % prototype)
def write_hierarchy(self, obj_name, ancestry, fp):
indent = ''
for name, interfaces in ancestry:
fp.write(indent + '+-- ' + name)
if interfaces:
fp.write(' (implements ')
fp.write(string.join(interfaces, ', '))
fp.write(')\n')
else:
fp.write('\n')
indent = indent + ' '
fp.write('\n')
def write_constructor(self, func_def, func_doc, fp):
prototype = self.create_constructor_prototype(func_def)
fp.write(prototype + '\n\n')
for type, name, dflt, null in func_def.params:
if func_doc:
descr = func_doc.get_param_description(name)
else:
descr = 'a ' + type
fp.write(' ' + name + ': ' + descr + '\n')
if func_def.ret and func_def.ret != 'none':
if func_doc and func_doc.ret:
descr = func_doc.ret
else:
descr = 'a ' + func_def.ret
fp.write(' Returns: ' + descr + '\n')
if func_doc and func_doc.description:
fp.write(func_doc.description)
fp.write('\n\n\n')
def write_method(self, meth_def, func_doc, fp):
prototype = self.create_method_prototype(meth_def)
fp.write(prototype + '\n\n')
for type, name, dflt, null in meth_def.params:
if func_doc:
descr = func_doc.get_param_description(name)
else:
descr = 'a ' + type
fp.write(' ' + name + ': ' + descr + '\n')
if meth_def.ret and meth_def.ret != 'none':
if func_doc and func_doc.ret:
descr = func_doc.ret
else:
descr = 'a ' + meth_def.ret
fp.write(' Returns: ' + descr + '\n')
if func_doc and func_doc.description:
fp.write('\n')
fp.write(func_doc.description)
fp.write('\n\n')
class DocbookDocWriter(DocWriter):
def __init__(self, use_xml=0):
DocWriter.__init__(self)
self.use_xml = use_xml
def create_filename(self, obj_name, output_prefix):
'''Create output filename for this particular object'''
stem = output_prefix + '-' + string.lower(obj_name)
if self.use_xml:
return stem + '.xml'
else:
return stem + '.sgml'
def create_toc_filename(self, output_prefix):
if self.use_xml:
return self.create_filename('classes', output_prefix)
else:
return self.create_filename('docs', output_prefix)
# make string -> reference translation func
__transtable = [ '-' ] * 256
for digit in '0123456789':
__transtable[ord(digit)] = digit
for letter in 'abcdefghijklmnopqrstuvwxyz':
__transtable[ord(letter)] = letter
__transtable[ord(string.upper(letter))] = letter
__transtable = string.join(__transtable, '')
def make_class_ref(self, obj_name):
return 'class-' + string.translate(obj_name, self.__transtable)
def make_method_ref(self, meth_def):
return 'method-' + string.translate(meth_def.of_object,
self.__transtable) + \
'--' + string.translate(meth_def.name, self.__transtable)
__function_pat = re.compile(r'(\w+)\s*\(\)')
def __format_function(self, match):
info = self.parser.c_name.get(match.group(1), None)
if info:
if isinstance(info, defsparser.FunctionDef):
if info.is_constructor_of is not None:
# should have a link here
return '%s()' % \
self.pyname(info.is_constructor_of)
else:
return '' + info.name + '()'
if isinstance(info, defsparser.MethodDef):
return '' + self.pyname(info.of_object) + '.' + \
info.name + '()'
# fall through through
return '' + match.group(1) + '()'
__parameter_pat = re.compile(r'\@(\w+)')
def __format_param(self, match):
return '' + match.group(1) + ''
__constant_pat = re.compile(r'\%(-?\w+)')
def __format_const(self, match):
return '' + match.group(1) + ''
__symbol_pat = re.compile(r'#([\w-]+)')
def __format_symbol(self, match):
info = self.parser.c_name.get(match.group(1), None)
if info:
if isinstance(info, defsparser.FunctionDef):
if info.is_constructor_of is not None:
# should have a link here
return '' + self.pyname(info.is_constructor_of) + \
''
else:
return '' + info.name + ''
if isinstance(info, defsparser.MethodDef):
return '' + self.pyname(info.of_object) + '.' + \
info.name + ''
if isinstance(info, defsparser.ObjectDef) or \
isinstance(info, defsparser.InterfaceDef) or \
isinstance(info, defsparser.BoxedDef) or \
isinstance(info, defsparser.PointerDef):
return '' + self.pyname(info.c_name) + \
''
# fall through through
return '' + match.group(1) + ''
def reformat_text(self, text, singleline=0):
# replace special strings ...
text = self.__function_pat.sub(self.__format_function, text)
text = self.__parameter_pat.sub(self.__format_param, text)
text = self.__constant_pat.sub(self.__format_const, text)
text = self.__symbol_pat.sub(self.__format_symbol, text)
# don't bother with expansion for single line text.
if singleline: return text
lines = string.split(string.strip(text), '\n')
for index in range(len(lines)):
if string.strip(lines[index]) == '':
lines[index] = '\n'
continue
lines.insert(0, '')
lines.append('')
return string.join(lines, '\n')
# write out hierarchy
def write_full_hierarchy(self, hierarchy, fp):
def handle_node(node, fp, indent=''):
if node.name:
fp.write('%s%s' %
(indent, self.make_class_ref(node.name),
self.pyname(node.name)))
if node.interfaces:
fp.write(' (implements ')
for i in range(len(node.interfaces)):
fp.write('%s' %
(self.make_class_ref(node.interfaces[i]),
self.pyname(node.interfaces[i])))
if i != len(node.interfaces) - 1:
fp.write(', ')
fp.write(')\n')
else:
fp.write('\n')
indent = indent + ' '
node.subclasses.sort(lambda a,b:
cmp(self.pyname(a.name), self.pyname(b.name)))
for child in node.subclasses:
handle_node(child, fp, indent)
if self.use_xml:
fp.write('\n')
fp.write('\n')
fp.write('')
handle_node(hierarchy, fp)
fp.write('\n')
# these need to handle default args ...
def create_constructor_prototype(self, func_def):
sgml = [ '\n']
sgml.append(' __init__\n')
for type, name, dflt, null in func_def.params:
sgml.append(' ')
sgml.append(name)
sgml.append('')
if dflt:
sgml.append('')
sgml.append(dflt)
sgml.append('')
sgml.append('\n')
if not func_def.params:
sgml.append(' ')
sgml.append(' ')
return string.join(sgml, '')
def create_function_prototype(self, func_def):
sgml = [ '\n \n']
sgml.append(' ')
sgml.append(func_def.name)
sgml.append('\n')
for type, name, dflt, null in func_def.params:
sgml.append(' ')
sgml.append(name)
sgml.append('')
if dflt:
sgml.append('')
sgml.append(dflt)
sgml.append('')
sgml.append('\n')
if not func_def.params:
sgml.append(' \n ')
return string.join(sgml, '')
def create_method_prototype(self, meth_def, addlink=0):
sgml = [ '\n']
sgml.append(' ')
if addlink:
sgml.append('' % self.make_method_ref(meth_def))
sgml.append(self.pyname(meth_def.name))
if addlink:
sgml.append('')
sgml.append('\n')
for type, name, dflt, null in meth_def.params:
sgml.append(' ')
sgml.append(name)
sgml.append('')
if dflt:
sgml.append('')
sgml.append(dflt)
sgml.append('')
sgml.append('\n')
if not meth_def.params:
sgml.append(' ')
sgml.append(' ')
return string.join(sgml, '')
def write_class_header(self, obj_name, fp):
if self.use_xml:
fp.write('\n')
fp.write('\n')
fp.write('\n')
fp.write(' \n')
fp.write(' %s\n'
% self.pyname(obj_name))
fp.write(' 3\n')
fp.write(' PyGTK Docs\n')
fp.write(' \n\n')
fp.write(' \n')
fp.write(' %s\n'
% self.pyname(obj_name))
fp.write(' \n\n')
def write_class_footer(self, obj_name, fp):
fp.write('\n')
def write_heading(self, text, fp):
fp.write(' \n')
fp.write(' ' + text + '\n\n')
def close_section(self, fp):
fp.write(' \n')
def write_synopsis(self, obj_def, fp):
fp.write('\n')
fp.write(' %s\n'
% self.pyname(obj_def.c_name))
if isinstance(obj_def, definitions.ObjectDef):
if obj_def.parent:
fp.write(' %s'
'\n'
% (self.make_class_ref(obj_def.parent),
self.pyname(obj_def.parent)))
for base in obj_def.implements:
fp.write(' %s'
'\n'
% (self.make_class_ref(base), self.pyname(base)))
elif isinstance(obj_def, definitions.InterfaceDef):
fp.write(' gobject.GInterface'
'\n')
elif isinstance(obj_def, definitions.BoxedDef):
fp.write(' gobject.GBoxed'
'\n')
elif isinstance(obj_def, definitions.PointerDef):
fp.write(' gobject.GPointer'
'\n')
constructor = self.parser.find_constructor(obj_def, self.overrides)
if constructor:
fp.write('%s\n' % self.create_constructor_prototype(constructor))
methods = self.parser.find_methods(obj_def)
methods = filter(lambda meth, self=self:
not self.overrides.is_ignored(meth.c_name), methods)
for meth in methods:
fp.write('%s\n' % self.create_method_prototype(meth, addlink=1))
fp.write('\n\n')
def write_hierarchy(self, obj_name, ancestry, fp):
fp.write('')
indent = ''
for name, interfaces in ancestry:
fp.write(indent + '+-- '+ self.pyname(name) + '')
if interfaces:
fp.write(' (implements ')
for i in range(len(interfaces)):
fp.write('%s' %
(self.make_class_ref(interfaces[i]),
self.pyname(interfaces[i])))
if i != len(interfaces) - 1:
fp.write(', ')
fp.write(')\n')
else:
fp.write('\n')
indent = indent + ' '
fp.write('\n\n')
def write_params(self, params, ret, func_doc, fp):
if not params and (not ret or ret == 'none'):
return
fp.write(' \n')
for type, name, dflt, null in params:
if func_doc:
descr = string.strip(func_doc.get_param_description(name))
else:
descr = 'a ' + type
fp.write(' \n')
fp.write(' %s :\n' % name)
fp.write(' %s\n' %
self.reformat_text(descr, singleline=1))
fp.write(' \n')
if ret and ret != 'none':
if func_doc and func_doc.ret:
descr = string.strip(func_doc.ret)
else:
descr = 'a ' + ret
fp.write(' \n')
fp.write(' Returns :\n')
fp.write(' %s\n' %
self.reformat_text(descr, singleline=1))
fp.write(' \n')
fp.write(' \n')
def write_constructor(self, func_def, func_doc, fp):
prototype = self.create_constructor_prototype(func_def)
fp.write('%s\n' % prototype)
self.write_params(func_def.params, func_def.ret, func_doc, fp)
if func_doc and func_doc.description:
fp.write(self.reformat_text(func_doc.description))
fp.write('\n\n\n')
def write_method(self, meth_def, func_doc, fp):
fp.write(' \n')
fp.write(' ' + self.pyname(meth_def.of_object) + '.' +
meth_def.name + '\n\n')
prototype = self.create_method_prototype(meth_def)
fp.write('%s\n' % prototype)
self.write_params(meth_def.params, meth_def.ret, func_doc, fp)
if func_doc and func_doc.description:
fp.write(self.reformat_text(func_doc.description))
fp.write(' \n\n\n')
def output_toc(self, files, fp=sys.stdout):
if self.use_xml:
fp.write('\n')
fp.write('\n')
#for filename, obj_def in files:
# fp.write(' \n')
#fp.write(']>\n\n')
#fp.write('\n')
#fp.write(' Class Documentation\n')
#for filename, obj_def in files:
# fp.write('&' + string.translate(obj_def.c_name,
# self.__transtable) + ';\n')
#fp.write('\n')
fp.write('\n')
fp.write(' Class Reference\n')
for filename, obj_def in files:
fp.write(' \n' % filename)
fp.write('\n')
else:
fp.write('\n')
fp.write(']>\n\n')
fp.write('\n\n')
fp.write(' \n')
fp.write(' PyGTK Docs\n')
fp.write(' \n')
fp.write(' \n')
fp.write(' James\n')
fp.write(' Henstridge\n')
fp.write(' \n')
fp.write(' \n')
fp.write(' \n\n')
fp.write(' \n')
fp.write(' Class Hierarchy\n')
fp.write(' Not done yet\n')
fp.write(' \n\n')
fp.write(' \n')
fp.write(' Class Documentation\n')
for filename, obj_def in files:
fp.write('&' + string.translate(obj_def.c_name,
self.__transtable) + ';\n')
fp.write(' \n')
fp.write('\n')
if __name__ == '__main__':
try:
opts, args = getopt.getopt(sys.argv[1:], "d:s:o:",
["defs-file=", "override=", "source-dir=",
"output-prefix="])
except getopt.error, e:
sys.stderr.write('docgen.py: %s\n' % e)
sys.stderr.write(
'usage: docgen.py -d file.defs [-s /src/dir] [-o output-prefix]\n')
sys.exit(1)
defs_file = None
overrides_file = None
source_dirs = []
output_prefix = 'docs'
for opt, arg in opts:
if opt in ('-d', '--defs-file'):
defs_file = arg
if opt in ('--override',):
overrides_file = arg
elif opt in ('-s', '--source-dir'):
source_dirs.append(arg)
elif opt in ('-o', '--output-prefix'):
output_prefix = arg
if len(args) != 0 or not defs_file:
sys.stderr.write(
'usage: docgen.py -d file.defs [-s /src/dir] [-o output-prefix]\n')
sys.exit(1)
d = DocbookDocWriter()
d.add_sourcedirs(source_dirs)
d.add_docs(defs_file, overrides_file, 'gtk')
d.output_docs(output_prefix)