##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
API documentation help topics
"""
import types
import HelpTopic
from Globals import DTMLFile,Persistent
_ignore_objects = {}
try:
import Interface
_ignore_objects.update(Interface.__dict__)
except ImportError:
pass
class APIHelpTopic(HelpTopic.HelpTopic):
"""
Provides API documentation.
"""
isAPIHelpTopic=1
funcs=() # for backward compatibility
def __init__(self, id, title, file):
self.id=id
self.title=title
dict={}
execfile(file, dict)
self.doc=dict.get('__doc__','')
self.apis=[]
self.funcs=[]
for k, v in dict.items():
if (not _ignore_objects.has_key(k) or
_ignore_objects[k] is not v):
if type(v)==types.ClassType:
# A class.
self.apis.append(APIDoc(v, 0))
elif (hasattr(v, 'isImplementedByInstancesOf')):
# A scarecrow interface.
self.apis.append(APIDoc(v, 1))
elif type(v)==types.FunctionType:
# A function
self.funcs.append(MethodDoc(v, 0))
# try to get title from first non-blank line
# of module docstring
if not self.title:
lines=self.doc.split('\n')
while 1:
line=lines[0].strip()
if line:
# get rid of anything after a colon in the line
self.title=line.split(':')[0]
break
lines.pop(0)
if not lines:
break
# otherwise get title from first class name
if not self.title:
self.title=self.apis[0].name
index_html=DTMLFile('dtml/APIHelpView', globals())
def SearchableText(self):
"The full text of the Help Topic, for indexing purposes"
text="%s %s" % (self.title, self.doc)
for api in self.apis + self.funcs:
text="%s %s" % (text, api.SearchableText())
return text
class APIDoc(Persistent):
"""
Describes an API.
"""
extends=()
def __init__(self, klass, isInterface=0):
if isInterface:
self._createFromInterface(klass)
else:
self._createFromClass(klass)
def _createFromInterface(self, klass):
# Creates an APIDoc instance given an interface object.
self.name=klass.__name__
self.doc=trim_doc_string(klass.__doc__)
# inheritence information
self.extends=[]
## for base in klass.getBases():
## names = string.split(base.__name__, '.')
## url="%s/Help/%s.py#%s" % (names[0], names[1], names[2])
## self.extends.append((names[2], url))
# constructor information
## if hasattr(klass, '__constructor__'):
## self.constructor=MethodDoc(klass.__constructor__)
# Get info on methods and attributes, ignore special items
self.attributes=[]
self.methods=[]
from Interface.Method import Method
for k,v in klass.namesAndDescriptions():
if hasattr(v, 'getSignatureInfo'):
self.methods.append(MethodDoc(v, 1))
else:
self.attributes.append(AttributeDoc(k, v.__doc__))
def _createFromClass(self, klass):
# Creates an APIDoc instance given a python class.
# the class describes the API; it contains
# methods, arguments and doc strings.
#
# The name of the API is deduced from the name
# of the class.
#
# The base APIs are deduced from the __extends__
# attribute.
self.name=klass.__name__
self.doc=trim_doc_string(klass.__doc__)
# inheritence information
if hasattr(klass,'__extends__'):
self.extends=[]
for base in klass.__extends__:
names=base.split( '.')
url="%s/Help/%s.py#%s" % (names[0], names[1], names[2])
self.extends.append((names[2], url))
# constructor information
if hasattr(klass, '__constructor__'):
self.constructor=MethodDoc(klass.__constructor__)
# Get info on methods and attributes, ignore special items
self.attributes=[]
self.methods=[]
for k,v in klass.__dict__.items():
if k not in ('__extends__', '__doc__', '__constructor__'):
if type(v)==types.FunctionType:
self.methods.append(MethodDoc(v, 0))
else:
self.attributes.append(AttributeDoc(k, v))
def SearchableText(self):
"""
The full text of the API, for indexing purposes.
"""
text="%s %s" % (self.name, self.doc)
for attribute in self.attributes:
text="%s %s" % (text, attribute.name)
for method in self.methods:
text="%s %s %s" % (text, method.name, method.doc)
return text
view=DTMLFile('dtml/APIView', globals())
class AttributeDoc(Persistent):
"""
Describes an attribute of an API.
"""
def __init__(self, name, value):
self.name=name
self.value=value
view=DTMLFile('dtml/attributeView', globals())
class MethodDoc(Persistent):
"""
Describes a method of an API.
required - a sequence of required arguments
optional - a sequence of tuples (name, default value)
varargs - the name of the variable argument or None
kwargs - the name of the kw argument or None
"""
varargs=None
kwargs=None
def __init__(self, func, isInterface=0):
if isInterface:
self._createFromInterfaceMethod(func)
else:
self._createFromFunc(func)
def _createFromInterfaceMethod(self, func):
self.name = func.__name__
self.doc = trim_doc_string(func.__doc__)
self.required = func.required
opt = []
for p in func.positional[len(func.required):]:
opt.append((p, func.optional[p]))
self.optional = tuple(opt)
if func.varargs:
self.varargs = func.varargs
if func.kwargs:
self.kwargs = func.kwargs
def _createFromFunc(self, func):
if hasattr(func, 'im_func'):
func=func.im_func
self.name=func.__name__
self.doc=trim_doc_string(func.__doc__)
# figure out the method arguments
# mostly stolen from pythondoc
CO_VARARGS = 4
CO_VARKEYWORDS = 8
names = func.func_code.co_varnames
nrargs = func.func_code.co_argcount
if func.func_defaults:
nrdefaults = len(func.func_defaults)
else:
nrdefaults = 0
self.required = names[:nrargs-nrdefaults]
if func.func_defaults:
self.optional = tuple(map(None, names[nrargs-nrdefaults:nrargs],
func.func_defaults))
else:
self.optional = ()
varargs = []
ix = nrargs
if func.func_code.co_flags & CO_VARARGS:
self.varargs=names[ix]
ix = ix+1
if func.func_code.co_flags & CO_VARKEYWORDS:
self.kwargs=names[ix]
view=DTMLFile('dtml/methodView', globals())
def trim_doc_string(text):
"""
Trims a doc string to make it format
correctly with structured text.
"""
text=text.strip()
text=text.replace( '\r\n', '\n')
lines=text.split('\n')
nlines=[lines[0]]
if len(lines) > 1:
min_indent=None
for line in lines[1:]:
if not line:
continue
indent=len(line) - len(line.lstrip())
if indent < min_indent or min_indent is None:
min_indent=indent
for line in lines[1:]:
nlines.append(line[min_indent:])
return '\n'.join(nlines)
|