#!/usr/bin/env python
# -*- Mode: Python; py-indent-offset: 4 -*-
import sys, os, string, re, getopt
import defsparser
import override
import docextract
class DocWriter:
def __init__(self, defs_file, overrides_file, source_dirs):
# parse the defs file
self.parser = defsparser.DefsParser(defs_file)
self.parser.startParsing()
# the overrides file
if overrides_file:
self.overrides = override.Overrides(open(overrides_file))
else:
self.overrides = override.Overrides(None)
# extract documentation from C source code
self.docs = docextract.extract(source_dirs)
def output_docs(self, output_prefix):
obj_defs = self.parser.objects[:]
obj_defs.sort(lambda a, b: cmp(a.c_name, b.c_name))
files = []
for obj_def in obj_defs:
filename = self.create_filename(obj_def.c_name, output_prefix)
fp = open(filename, 'w')
self.output_object_docs(obj_def, fp)
fp.close()
files.append((os.path.basename(filename), obj_def))
if files:
filename = self.create_filename('docs', 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)
# construct the inheritence heirachy ...
ancestry = [ (obj_def.c_name, obj_def.implements) ]
try:
parent = obj_def.parent
while parent != (None, None):
if parent[1] + parent[0] == 'GObject':
ancestry.append(('GObject', []))
parent = (None, None)
else:
parent_def = self.parser.find_object(parent[1] + parent[0])
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_heirachy(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_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'
# 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[1] + meth_def.of_object[0] + '.' + \
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_heirachy(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 create_filename(self, obj_name, output_prefix):
'''Create output filename for this particular object'''
return output_prefix + '-' + string.lower(obj_name) + '.sgml'
# make string -> reference translation func
__transtable = [ '-' ] * 256
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[1] +
meth_def.of_object[0],
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 info.__class__ == defsparser.FunctionDef:
if info.is_constructor_of is not None:
# should have a link here
return '' + info.is_constructor_of + \
'()'
else:
return '' + info.name + '()'
if info.__class__ == defsparser.MethodDef:
return '' + info.of_object[1] + \
info.of_object[0] + '.' + 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 info.__class__ == defsparser.FunctionDef:
if info.is_constructor_of is not None:
# should have a link here
return '' + info.is_constructor_of + \
''
else:
return '' + info.name + ''
if info.__class__ == defsparser.MethodDef:
return '' + info.of_object[1] + \
info.of_object[0] + '.' + info.name + \
''
if info.__class__ == defsparser.ObjectDef:
return '' + info.c_name + ''
# fall through through
return '' + match.group(1) + ''
def reformat_text(self, text):
# 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)
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')
# these need to handle default args ...
def create_constructor_prototype(self, func_def):
sgml = [ ' \n \n']
sgml.append(' ')
sgml.append(func_def.is_constructor_of)
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('\n')
if not func_def.params:
sgml.append(' ')
sgml.append(' \n \n')
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('\n')
if not func_def.params:
sgml.append(' ')
sgml.append(' \n \n')
return string.join(sgml, '')
def create_method_prototype(self, meth_def):
sgml = [ ' \n \n']
sgml.append(' instance.')
sgml.append(meth_def.name)
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('\n')
if not meth_def.params:
sgml.append(' ')
sgml.append(' \n \n')
return string.join(sgml, '')
def write_class_header(self, obj_name, fp):
fp.write('\n')
fp.write(' Class ' + obj_name + '\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_heirachy(self, obj_name, ancestry, fp):
fp.write('')
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\n')
def write_constructor(self, func_def, func_doc, fp):
prototype = self.create_constructor_prototype(func_def)
fp.write(prototype + '\n')
varlist_started = 0
for type, name, dflt, null in func_def.params:
if not varlist_started:
fp.write(' \n')
varlist_started = 1
if func_doc:
descr = string.strip(func_doc.get_param_description(name))
else:
descr = 'a ' + type
fp.write(' \n')
fp.write(' '+ name +'\n')
fp.write(' ' + self.reformat_text(descr) +
'\n')
fp.write(' \n')
varlist_started = 1
if func_doc and func_doc.ret:
descr = string.strip(func_doc.ret)
else:
descr = 'a ' + func_def.ret
fp.write(' \n')
fp.write(' Returns:\n')
fp.write(' ' + self.reformat_text(descr) +
'\n')
fp.write(' \n')
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(' ' + meth_def.of_object[1] +
meth_def.of_object[0] + '.' + meth_def.name + '\n\n')
prototype = self.create_method_prototype(meth_def)
fp.write(prototype + '\n')
varlist_started = 0
for type, name, dflt, null in meth_def.params:
if not varlist_started:
fp.write(' \n')
varlist_started = 1
if func_doc:
descr = string.strip(func_doc.get_param_description(name))
else:
descr = 'a ' + type
fp.write(' \n')
fp.write(' '+ name +'\n')
fp.write(' ' + self.reformat_text(descr) +
'\n')
fp.write(' \n')
varlist_started = 1
if func_doc and func_doc.ret:
descr = string.strip(func_doc.ret)
else:
descr = 'a ' + meth_def.ret
fp.write(' \n')
fp.write(' Returns:\n')
fp.write(' ' + self.reformat_text(descr) +
'\n')
fp.write(' \n')
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):
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 Heirachy\n')
fp.write(' Not done yet\n')
fp.write(' \n')
for filename, obj_def in files:
fp.write('&' +string.translate(obj_def.c_name, self.__transtable)+
';\n')
fp.write('\n')
if __name__ == '__main__':
opts, args = getopt.getopt(sys.argv[1:], "d:s:o:",
["defs-file=", "override=", "source-dir=",
"output-prefix="])
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]')
d = DocbookDocWriter(defs_file, overrides_file, source_dirs)
d.output_docs(output_prefix)