#!/usr/bin/env python
#
# $Id: appclass.py,v 1.17 2006/12/05 13:10:45 doughellmann Exp $
#
# Copyright Doug Hellmann 2000
#
# 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.
#
"""Command line application class for HappyDoc.
"""
__rcs_info__ = {
#
# Creation Information
#
'module_name' : '$RCSfile: appclass.py,v $',
'rcs_id' : '$Id: appclass.py,v 1.17 2006/12/05 13:10:45 doughellmann Exp $',
'creator' : 'Doug Hellmann',
'project' : 'HappyDoc',
'created' : 'Sun, 13-Aug-2000 11:27:00 EDT',
#
# Current Information
#
'author' : '$Author: doughellmann $',
'version' : '$Revision: 1.17 $',
'date' : '$Date: 2006/12/05 13:10:45 $',
}
try:
__version__ = __rcs_info__['version'].split(' ')[1]
except:
__version__ = '0.0'
#
# Import system modules
#
import glob
import mimetypes
import os
import re
import sys
import string
import traceback
import types
#
# Import Local modules
#
import happydoclib
from happydoclib.scanner import Scanner
from happydoclib import status
from happydoclib.trace import trace
#
# Module
#
class HappyDoc(happydoclib.CommandLineApp.CommandLineApp):
"""
HappyDoc is a documentation generation/extraction tool which does
not depend on being able to import modules.
The data extraction library used by this app is based on the
Demos/parser/example.py module distributed with the Python source
distribution.
"""
shortArgumentsDescription = 'file...'
include_private_names = True
include_comments = True
output_directory = './doc'
output = None
author_name = 'Doug Hellmann'
app_home = 'http://HappyDoc.sourceforge.net/'
package_description_file = 'README.txt'
docset_type = None
docset_title = 'HappyDoc Generated Documentation'
#
# Define the docstring syntaxes supported
#
docstring_syntaxes = happydoclib.docstring.DocStringLoader()
#
# Define the documentation set types supported
#
supported_docset_types = happydoclib.docset.DocSetLoader()
#
# Which template should be used?
#
template_name = None
#
# Where should we look for templates?
#
template_path = None
##
## Local methods
##
def appInit(self):
status.registerStatusMessageFunc(self.statusMessage)
self._app_name = self.__class__.__name__
self._app_version = happydoclib.cvsProductVersion()
self.set_docset_type('MultiHTMLFile')
#self.set_docset_type('TAL')
self._ignore_dir_patterns = Scanner.DEFAULT_IGNORE_PATTERNS[:]
#
# Initialize extensions for mimetypes that we know about but which are
# not standard.
#
self.addMimetype('stx', 'text/x-structured')
return
def addMimetype(self, extension, mimetypeSpec):
if extension and extension[0] != '.':
key = '.%s' % extension
else:
key = extension
mimetypes.types_map[key] = mimetypeSpec
return
def addIgnoreDirectoryPattern(self, *dirNamePatterns):
"Add one or more directory name patterns to the list which should be ignored."
for dir_name_pattern in dirNamePatterns:
if dir_name_pattern not in self._ignore_dir_patterns:
self._ignore_dir_patterns.append(dir_name_pattern)
self.statusMessage('Ignoring %s' % dir_name_pattern, 2)
return
def set_docset_type(self, docset_type):
"Set the docset to be used."
self.docset_type = docset_type
try:
self.docset_factory = self.supported_docset_types[docset_type]
except KeyError:
raise ValueError('docset_type must be one of %s' % \
self.supported_docset_types.keys(),
docset_type)
return
##
## Override CommandLineApp methods
##
def _showOptionItemsDescription(self, title, items):
items.sort()
for name, obj in items:
if obj.__doc__:
description = str(obj.__doc__).strip()
else:
description = ''
print ' %s %s: %s\n' % (title, name, description)
return
def showVerboseSyntaxHelp(self):
"Overloaded to show supported docset types."
happydoclib.CommandLineApp.CommandLineApp.showVerboseSyntaxHelp(self)
print 'SUPPORTED DOCSTRING SYNTAXES:\n'
self._showOptionItemsDescription('SYNTAX TYPE', self.docstring_syntaxes.items())
print 'SUPPORTED DOCSET TYPES for -T Option:'
print
print ' %s' % happydoclib.docset.base.DocSet.__doc__
print
self._showOptionItemsDescription(
'DOCSET TYPE', self.supported_docset_types.items())
print
print 'PARSER ARGUMENTS:'
print
print ' Parser arguments control the default behavior of the'
print ' documentation extraction parser. Pass the argument'
print ' as an argument on the command line using the syntax:'
print
print ' parser_<argument>=value'
print
print ' Arguments:'
print
print ' docStringFormat -- Name of the docstring converter'
print ' format used in the inline documentation.'
print ' Defaults to "StructuredText".'
print
return
##
## Argument handlers
##
## def optionHandler_author(self, authorNameAndEmail):
## """Specify the author identification to be inserted for
## references.
## """
## self.author_name = authorNameAndEmail
## return
def optionHandler_mimetype(self, extensionAndMimetype):
"""Specify a filename extension and mimetype mapping.
This is useful if input files are named in a way that the
Python mimetypes module cannot determine their mimetype
automatically.
For example::
--mimetype stx=text/x-structured
--mimetype .gif=image/gif
"""
parts = extensionAndMimetype.split('=')
if len(parts) != 2:
raise ValueError('Could not understand "%s". Use --mimetype "ext=mimetype"' % extensionAndMimetype)
self.addMimetype(parts[0], parts[1])
return
def optionHandler_d(self, outputDirectory):
"""Specify an outputDirectory.
Defaults to './doc'."""
self.output_directory = os.path.normcase(outputDirectory)
return
## def optionHandler_dia(self):
## """Generate UML diagram in Gnome dia format.
## """
## self.set_docset_type("Dia")
## self.set_format("Dia")
## return
def optionHandler_i(self, ignoreDirectory):
"""Specify a directory basename to be ignored.
Use just the base name of the directory.
For instance, to ignore all directories
with the name CVS, specify: -i CVS.
Defaults to ignore::
^(CVS|dist|build|docs?|.*pyc|.*~|tmp)$
trace.txt
"""
ignoreDirectory=string.strip(ignoreDirectory)
self.statusMessage('Adding ignore directive for %s' % ignoreDirectory)
self.addIgnoreDirectoryPattern(ignoreDirectory)
return
def optionHandler_no_comments(self):
"""Do not include comment text as though it was
a __doc__ string.
"""
happydoclib.parseinfo.setOption(include_comments=0)
return
def optionHandler_no_cache(self):
"""Disable caching of parse results.
"""
happydoclib.parseinfo.setOption(useCache=0)
return
def optionHandler_cache_prefix(self, cacheFilePrefix):
"""Set the prefix of parse cache files.
Defaults to '.happydoc.'
"""
happydoclib.parseinfo.setOption(cacheFilePrefix=cacheFilePrefix)
return
def optionHandler_no_private_names(self):
"Do not include names beginning with _."
self.include_private_names = False
return
## def optionHandler_o(self):
## "Specify that output should go to stdout."
## self.set_docset_type('StdOut')
## return
## def optionHandler_p(self, packageDescriptionFile):
## """Specify a file with a description of the package.
## The default packageDescriptionFile is README.txt.
## """
## self.package_description_file = packageDescriptionFile
## return
def optionHandler_title(self, title):
"Specify a title for the documentation set."
self.docset_title = title
return
def optionHandler_T(self, docset_type):
"""Specify the documentation set type.
Defaults to 'MultiHTMLFile'."""
self.set_docset_type(docset_type)
return
def optionHandler_t(self, template_name):
"""The name of the template set.
The value is expected to correspond to the name of a directory
containing a template set. If the path exists, it will be
used. If it does not exist, HappyDoc will look for a
directory with the same name in 'happydoclib/templates'.
"""
self.template_name = template_name
return
def optionHandler_template_path(self, template_path_directory):
"""Set the parent directory of the template directory.
"""
self.template_path = template_path_directory
return
##
## Main
##
def getParameterGroupsFromArguments(self, args):
#
# Set default parser params
#
parser_params = {
'docStringFormat':'StructuredText',
}
#
# Find parser arguments
#
self.statusMessage('Looking for parser parameters', 2)
args, user_supplied_parser_params = happydoclib.optiontools.getParameters(
'parser', args)
parser_params.update(user_supplied_parser_params)
self.statusMessage('DEBUG: Parser parameters:', 4)
for p, v in parser_params.items():
self.statusMessage('DEBUG: \t%s:%s' % (p,v), 4)
#
# Find DocSet arguments
#
self.statusMessage('Looking for docset parameters', 2)
args, docset_params = happydoclib.optiontools.getParameters('docset', args)
self.statusMessage('DEBUG: Docset parameters:', 4)
for p, v in docset_params.items():
self.statusMessage('DEBUG: \t%s:%s' % (p,v), 4)
return (args, parser_params, docset_params)
def main(self, *args):
self.statusMessage('%s version %s' % (self._app_name,
self._app_version))
parsed_args = self.getParameterGroupsFromArguments(args)
(args, parser_params, docset_params) = parsed_args
if self.template_name:
docset_params['template_name'] = self.template_name
if self.template_path:
docset_params['template_path'] = self.template_path
self.parser_params = parser_params
#
# Get the list of modules to input
#
if not args:
#
# No files specified, print a help message and exit.
#
self.showHelp('Specify input file(s) to be processed.')
raise self.HelpRequested, 'No input file(s) specified.'
else:
input_modules = []
for input_module_name in args:
normcase = os.path.normcase(input_module_name)
if not normcase:
continue
while normcase[-1] == os.sep:
normcase = normcase[:-1]
input_modules.append(normcase)
#
# Dump some basic info about what we are going to do.
#
self.statusMessage(verboseLevel=2)
self.statusMessage('Docset Title: %s' % self.docset_title,
verboseLevel=2)
self.statusMessage(verboseLevel=2)
self.statusMessage('Inputs:',
verboseLevel=2)
self.statusMessage(verboseLevel=2)
for input_module in input_modules:
if input_module == os.curdir:
input_module = os.getcwd()
self.statusMessage(' %s' % input_module,
verboseLevel=2)
self.statusMessage(verboseLevel=2)
self.statusMessage('Ignoring:', verboseLevel=2)
self.statusMessage(verboseLevel=2)
for ignore_pattern in self._ignore_dir_patterns:
self.statusMessage(' %s' % ignore_pattern,
verboseLevel=2)
self.statusMessage(verboseLevel=2)
self.statusMessage('Parameters:', verboseLevel=2)
self.statusMessage(verboseLevel=2)
if self.include_comments:
self.statusMessage(' Including comments',
verboseLevel=2)
if self.include_private_names:
self.statusMessage(' Including private symbol names',
verboseLevel=2)
extra_params = docset_params.items()
extra_params.sort()
for name, value in extra_params:
self.statusMessage(' %s=%s' % (name, value),
verboseLevel=2)
self.statusMessage(verboseLevel=2)
self.statusMessage('Output Directory:', verboseLevel=2)
self.statusMessage(verboseLevel=2)
self.statusMessage(' %s' % self.output_directory,
verboseLevel=2)
self.statusMessage(verboseLevel=2)
#
# Create the scanner, and get the package trees.
#
self.statusMessage('Scanning...')
scanner = Scanner(inputDirectories=input_modules,
ignorePatterns=self._ignore_dir_patterns,
includeComments=self.include_comments,
)
self.statusMessage('Done')
#
# Create the docset
#
docset = self.docset_factory(
scanner=scanner,
title=self.docset_title,
outputDirectory=self.output_directory,
includeComments=self.include_comments,
includePrivateNames=self.include_private_names,
statusMessageFunc=self.statusMessage,
extraParameters=docset_params,
)
#
# Generate some output
#
self.statusMessage('Writing...')
docset.write()
self.statusMessage('Done')
return
|