#!/usr/bin/env python
#
# $Id: packagetree.py,v 1.11 2006/12/05 13:10:45 doughellmann Exp $
#
# Copyright 2002 Doug Hellmann.
#
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of Doug
# Hellmann not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# DOUG HELLMANN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL DOUG HELLMANN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""Tree representing the input data.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: packagetree.py,v $',
'rcs_id' : '$Id: packagetree.py,v 1.11 2006/12/05 13:10:45 doughellmann Exp $',
'creator' : 'Doug Hellmann',
'project' : 'HappyDoc',
'created' : 'Sun, 29-Dec-2002 12:37:53 EST',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.11 $',
'date' : '$Date: 2006/12/05 13:10:45 $',
}
try:
__version__ = __rcs_info__['version'].split(' ')[1]
except:
__version__ = '0.0'
#
# Import system modules
#
import os
import pprint
import re
import UserDict
#
# Import Local modules
#
import happydoclib
from happydoclib.status import statusMessage
from happydoclib.trace import trace
from happydoclib.utils import getMimeType
#
# Module
#
TRACE_LEVEL=2
class PackageTree(UserDict.UserDict):
"""Tree of package information.
Access each child node using the standard mapping de-reference
syntax ([], .get(), etc.).
"""
def __init__(self, parent, name):
trace.into('PackageTree', '__init__',
name=name,
outputLevel=TRACE_LEVEL,
)
UserDict.UserDict.__init__(self)
self.name = os.path.basename(name)
self.parent = parent
if self.parent is not None:
#
# If we have a parent, tell it about
# ourself and use the basename of
# our name as the canonical name.
#
self.parent.addSubNode(self)
self.canonical_name = self.name
else:
#
# We have no parent, so use our full
# name as the canonical name.
#
self.canonical_name = name
trace.write('self.name=%s' % self.name, outputLevel=TRACE_LEVEL)
trace.outof(outputLevel=TRACE_LEVEL)
return
def __repr__(self):
#base_str = UserDict.UserDict.__repr__(self)
base_str = ''
return '<%s %s: %s>' % (self.__class__.__name__, self.getName(), base_str)
def __len__(self):
"""Define this to always return 1, so checks like 'if node' work properly.
"""
return 1
#
# Data retrieval methods
#
def getMimeType(self):
"""Returns the (mimetype, encoding) setting for this node.
"""
trace.into('PackageTreeNode', 'getMimeType',
name=self.name,
outputLevel=TRACE_LEVEL,
)
input_filename = self.getInputFilename()
mimetype, encoding = getMimeType(input_filename)
trace.outof(mimetype, outputLevel=TRACE_LEVEL)
return (mimetype, encoding)
def getName(self):
"""Returns the name of this tree node.
"""
return self.name
def getCanonicalName(self):
"""Returns the canonical, full, name of the this tree node.
"""
return self.canonical_name
def getInputFilename(self):
"""Returns the original input filename that created the node.
"""
trace.into('PackageTree', 'getInputFilename',
outputLevel=TRACE_LEVEL)
node_path = self.getPath(1)
trace.writeVar(node_path=node_path, outputLevel=TRACE_LEVEL)
filename = apply(os.path.join, node_path)
trace.writeVar(filename=filename, outputLevel=TRACE_LEVEL)
trace.outof(filename, outputLevel=TRACE_LEVEL)
return filename
def getRelativeFilename(self):
"""Returns the filename relative to the root of the input area.
"""
trace.into('PackageTree', 'getRelativeFilename',
outputLevel=TRACE_LEVEL)
node_path = self.getPath()
trace.writeVar(node_path=node_path,
outputLevel=TRACE_LEVEL)
filename = apply(os.path.join, node_path)
trace.writeVar(filename=filename,
outputLevel=TRACE_LEVEL)
trace.outof(filename, outputLevel=TRACE_LEVEL)
return filename
def getDocStringAndFormat(self):
"""Returns a tuple containing the actual documentation string
and the format of that docstring as understood by the
docstring converters plugins.
"""
raise NotImplementedError('getDocStringAndFormat')
def getSummaryAndFormat(self):
"""Returns a tuple containing a one line summary of the
documentation for the node and the format of that
string as understood by the docstring converter plugins.
"""
raise NotImplementedError('getSummaryAndFormat')
def getOneLiner(self):
return self.getSummaryAndFormat()[0]
def getImportData(self):
"Returns a list of the symbols which are imported."
return []
#
# Tree methods
#
def getParent(self):
"""Returns the parent node for this tree.
If there is no parent (root of the tree), returns None.
"""
return self.parent
def getPath(self, useCanonicalName=0):
"""Return the path from the root to this node.
Returns a tuple of node names, beginning with the root node
and ending with this node.
"""
trace.into('PackageTree', 'getPath',
useCanonicalName=useCanonicalName,
outputLevel=TRACE_LEVEL+1,
)
parent = self.getParent()
trace.writeVar(parent=parent,
outputLevel=TRACE_LEVEL+1,
)
if parent:
parent_path = parent.getPath(useCanonicalName=useCanonicalName)
else:
parent_path = ()
trace.writeVar(parent_path=parent_path,
outputLevel=TRACE_LEVEL+1,
)
if useCanonicalName:
name = self.getCanonicalName()
else:
name = self.getName()
path = parent_path + (name,)
trace.outof(path,
outputLevel=TRACE_LEVEL+1,
)
return path
def getPathToNode(self, otherNode):
"""Returns a sequence of nodes to be traversed to reach the otherNode.
The sequence assumes that traversal begins at the current node.
A '..' indicates moving up to the parent one level.
"""
trace.into('PackageTree', 'getPathToNode',
otherNode=otherNode.getName(),
outputLevel=TRACE_LEVEL,
)
my_path = self.getPath()
other_path = otherNode.getPath()
if my_path == other_path:
trace.outof((), outputLevel=TRACE_LEVEL)
return ()
#
# Strip the top parts of the paths which match.
#
while my_path and other_path and my_path[0] == other_path[0]:
trace.write('Removing %s from both paths' % my_path[0],
outputLevel=TRACE_LEVEL)
my_path = my_path[1:]
other_path = other_path[1:]
trace.writeVar(my_path=my_path,
other_path=other_path,
outputLevel=TRACE_LEVEL,
)
if not other_path:
#
# Special Case: I am a child of the other node.
#
trace.write('source is child of dest',
outputLevel=TRACE_LEVEL)
if self.getMimeType()[0] == 'application/x-directory':
is_dir = 1
else:
is_dir = 0
trace.writeVar(is_dir=is_dir,
outputLevel=TRACE_LEVEL)
going_up = ('..',) * (len(my_path) + is_dir)
other_path = (otherNode.getName(),)
else:
#
# Either a parent or off in another branch of the
# tree.
#
if self.getMimeType()[0] == 'application/x-directory':
is_file = 0
else:
is_file = 1
going_up = ('..',) * (len(my_path) - is_file)
#
# Go up to a common point in the tree,
# and then back down to the other node.
#
relative_path = going_up + other_path
trace.outof(relative_path, outputLevel=TRACE_LEVEL)
return relative_path
def findNodeFromDottedName(self, dottedNodeName,
tryParent=1,
tryModuleExtensions=1,
):
"""Find the node referenced by the dotted name given.
The dottedNodeName 'happydoclib.scanner' retuns the node for
this module. If the named node cannot be found, the return
value is None.
"""
trace_level=TRACE_LEVEL
trace.into('PackageTree', 'findNodeFromDottedName',
dottedNodeName=dottedNodeName,
outputLevel=trace_level,
start=self.getName(),
)
name_parts = dottedNodeName.split('.')
name = name_parts[0]
trace.write('name=%s' % name,
outputLevel=trace_level,
)
trace.writeVar(name=name,
outputLevel=trace_level,
)
named_node = None
#
# Is someone looking for us?
#
if (named_node is None):
if (name == self.getName()):
trace.write('Matched ourself',
outputLevel=trace_level,
)
named_node = self
if (named_node is None):
trace.write('Checking %s as child' % name,
outputLevel=trace_level,
)
named_node = self.get(name)
if (named_node is None) and tryModuleExtensions:
trace.write('Checking %s.py as child' % name,
outputLevel=trace_level,
)
named_node = self.get('%s.py' % name)
if (named_node is None) and tryParent:
#
# Try the parent with the whole name
#
parent = self.getParent()
if parent:
trace.write('Checking for %s in parent' % name,
outputLevel=trace_level,
)
named_node = parent.findNodeFromDottedName(dottedNodeName)
#
# Do we need to process the remainder of the original name?
#
if named_node and (len(name_parts) > 1):
remainder = '.'.join(name_parts[1:])
trace.write('Handling remainder (%s)' % remainder,
outputLevel=trace_level,
)
named_node = named_node.findNodeFromDottedName(remainder,
tryParent=0)
if named_node is not None:
trace.outof(named_node.getName(),
outputLevel=trace_level,
)
else:
trace.outof(outputLevel=trace_level)
return named_node
def addSubNode(self, node):
"""Insert a child node under this node.
Create a new PackageTree node, set its parent to be ourself,
and save it as one of our children.
"""
self[node.getName()] = node
return node
def getSubNodes(self, mimetypes=[]):
"""Returns the children of this node.
If mimetype is not none, returns only the children with that
mimetype.
"""
if not mimetypes:
return self.values()
else:
if not hasattr(self, 'grouped_children'):
self.grouped_children = {}
for node in self.values():
node_mimetype, node_encoding = node.getMimeType()
group = self.grouped_children.setdefault(node_mimetype, [])
group.append(node)
subnodes = []
for mimetype in mimetypes:
subnodes += self.grouped_children.get(mimetype, [])
subnodes.sort()
return subnodes
def __cmp__(self, other):
"""Comparison by name for sorting.
The name check is case insensitive.
"""
return cmp(self.getName().lower(), other)
def walk(self, callback):
"""Walk the PackageTree, calling the callback at each node.
"""
callback(self)
for child in self.values():
child.walk(callback)
return
class CodeObjectTree(PackageTree):
"""Represents a code object to be documented.
"""
MIMETYPE = 'application/x-code'
def __init__(self, parent, name):
"""Construct a CodeObjectTree.
"""
trace.into('CodeObjectTree', '__init__',
name=name,
outputLevel=TRACE_LEVEL,
)
PackageTree.__init__(self, parent, name)
self.code_info = self._getCodeInfo()
trace.write(self.getMimeType()[0], outputLevel=TRACE_LEVEL)
trace.outof(outputLevel=TRACE_LEVEL)
return
def getMimeType(self):
return (self.MIMETYPE, None)
def getInputFilename(self):
"""Returns the input filename of the parent node.
"""
return self.getParent().getInputFilename()
def getRelativeFilename(self):
"""Returns the filename of the parent node.
"""
return self.getParent().getRelativeFilename()
def getDocStringAndFormat(self):
"""Returns the docstring of the code object, and the format of that data.
"""
return self.code_info.getDocStringAndFormat()
def getSummaryAndFormat(self):
"""Returns a tuple containing a one line summary of the
documentation for the node and the format of that
string as understood by the docstring converter plugins.
"""
return self.code_info.getSummaryAndFormat()
class FunctionTree(CodeObjectTree):
"""Represents a function in the package tree.
"""
MIMETYPE = 'application/x-function'
def _getCodeInfo(self):
return self.getParent().module_info.getFunctionInfo(self.name)
class MethodTree(FunctionTree):
"""Represents a method in the package tree.
"""
def _getCodeInfo(self):
return self.getParent().code_info.getMethodInfo(self.name)
class ClassTree(CodeObjectTree):
"""Represent a class in the package tree.
"""
MIMETYPE = 'application/x-class'
def __init__(self, parent, name):
trace.into('ClassTree', '__init__',
name=name,
outputLevel=TRACE_LEVEL,
)
CodeObjectTree.__init__(self, parent, name)
for method_name in self.code_info.getMethodNames():
method_node = MethodTree(self, method_name)
trace.outof(outputLevel=TRACE_LEVEL)
return
def _getCodeInfo(self):
return self.getParent().module_info.getClassInfo(self.name)
def getRelativeFilename(self):
"""Classes are written to their own file.
"""
return PackageTree.getRelativeFilename(self)
# def getRelativeFilename(self):
# """Returns the filename of this node.
# """
# parent_filename = self.getParent().getRelativeFilename()
# parent_filename, ext = os.path.splitext(parent_filename)
# my_filename = os.path.join(parent_filename, self.getName())
# return my_filename
|