#
# $Header: /usr/local/cvsroot/pythondoc/pythondoc.py,v 1.3 1999/05/01 00:58:20 daniel Exp $
#
# Copyright (C) Daniel Larsson
# All Rights Reserved.
#
# See copyright notice in the file 'LICENSE.TXT', which should have accompanied
# this distribution.
#
"""
SYNOPSIS

 Usage: *%(_program)s* [-a] [-D] [-d dir] [-f format] [-h] [-i] [-l] [-o option_file] [-s dir] [-v] [key=value ...] files ...

 **-a**, **--all** -- Generate documentation for all modules in sys.path.
 **-D**, **--nodoc** -- Don't generate document pages.
 **-d** *dir*, **--directory** *dir* -- Save generated files in the *dir* directory.
 **-f** *format*, **--format** *format* -- Generate files using the formatter *format*.
 **-h**, **--help** -- Print help text.
 **-i**, **--index** -- Generate index pages.
 **-l**, **--load** -- Load previously saved doctrees.
 **-o** *option file*, **--options** *option file* -- Load options from given file.
 **-s** *dir*, **--savedir** *dir* -- Save/read doctrees from given directory.
 **-v**, **--verbose** -- Verbose mode
 *key=value* -- Pass formatter options.

DESCRIPTION

 *%(_program)s* scans one or several python modules for documentation strings.
 From the documentation strings, manual pages are generated in any of
 FrameMaker MIF, MML, HTML and ASCII formats.

 Note that you can give multiple format options at once. It is a lot
 faster, since the the source files are only parsed/imported and traversed
 once.

 The *key=value* options are used to send arguments to formatters. These
 definitions can be put in the environment as well, where 'key' is an
 environment variable. The 'key' can either be '<module>_<keyname>', or
 just '<keyname>'. Options can also be read from option files. Option
 files are read from the directory /etc/pythondoc and your home directory
 (named pythondoc.opts), or by giving a filename with the **-o** option.

 The 'files' argument may either be module names or file names. Be careful
 if you use filenames. Internally, filenames are translated to module names,
 and the directory holding the file is added to sys.path. It is possible
 that a different module with the same name is loaded.

%(_options)sFORMATTERS

 Currently the following formatters are supported:

%(_formatters)s
"""


# Standard modules
import sys, os, string

# pythondoc modules
import docobjects, formatters, xref, options, stdmarkup, message, version

__author__ = "Daniel Larsson, Daniel.Larsson@telia.com"
__version__ = "$Revision: 1.3 $"[11:-2]
_program = os.path.basename(sys.argv[0])

copyright_txt = """%s, %s

Copyright Daniel Larsson, 1998
""" % (_program, version.__version__)

# Create options database
_options = options.options()

# Initialize formatter extensions
formatters.init(_options)

# Patch the __doc__ text with available formatters
import formatters.format_init
_formatters = ''
for formatter in formatters.format_init.formatters.keys():
    if formatter[0] != '_':
	_formatters = _formatters + ' * %s\n\n' % formatter

# Add main options.
_options.add_option('main', 'directory', _options.DIR,
		    'Directory to store output', '.')
_options.add_option('main', 'document', _options.BOOL,
		    'Generate document pages', 1)
_options.add_option('main', 'all', _options.BOOL,
		    'Traverse sys.path and document everything', 0)
_options.add_option('main', 'index', _options.BOOL, 
		    'Generate index pages', 0)
_options.add_option('main', 'head', _options.BOOL,
		    'Generate head page', 0)
_options.add_option('main', 'load', _options.BOOL,
		    'Load previously saved doctrees', 0)
_options.add_option('main', 'savedir', _options.DIR,
		    'Save doctrees', '')
_options.add_option('main', 'options', _options.FILE,
		    'Pythondoc option file', '')

__doc__ = __doc__ % vars() # Update module docstring with dynamic information


def parse_commandline():
    """Parse command line options.

    Returns a tuple containing arguments and a list of formatters."""
    import getopt

    # Process command line arguments
    try:
	optlist, args = getopt.getopt(sys.argv[1:], '?ad:Df:hilo:s:v',
	                              ['all', 'directory=', 'nodoc',
				       'format=', 'help', 'index', 'load',
                                       'options=', 'savedir=', 'verbose'])
    except getopt.error, msg:
	sys.stderr.write('%s: %s\n' % (_program, msg))
	sys.stderr.write(__doc__)
	sys.exit(-1)
    
    formats = []
    head = None

    # Set up verbose level first!
    for opt in optlist:
	if opt[0] == '-v' or opt[0] == '--verbose':
	    message.VERBOSE = message.VERBOSE + 1

    if message.VERBOSE > 0:
	print copyright_txt

    # Read options from file
    options.read_default_options("pythondoc.opts")

    for opt in optlist:
	if opt[0] in ('-h', '-?', '--help'):
	    print copyright_txt
	    print __doc__
	    sys.exit(0)
	elif opt[0] == '-a' or opt[0] == '--all':
	    _options.add_value('all', 1)
	elif opt[0] == '-d' or opt[0] == '--directory':
	    _options.add_value('main_directory', opt[1])
	elif opt[0] == '-D' or opt[0] == '--nodoc':
	    _options.add_value('main_document', 0)
	elif opt[0] == '-f' or opt[0] == '--format':
	    add_formatter(opt[1], formats)
	elif opt[0] == '-i' or opt[0] == '--index':
	    _options.add_value('main_index', 1)
	elif opt[0] == '-l' or opt[0] == '--load':
	    _options.add_value('main_load', 1)
	elif opt[0] == '-o' or opt[0] == '--options':
	    _options.add_value('main_options', opt[1])
	elif opt[0] == '-s' or opt[0] == '--savedir':
	    savedir = opt[1]
	    _options.add_value('main_savedir', savedir)
	    add_formatter('_Save', formats)

    # Allow options to be passed as environment variables
    import os
    try:
	map(lambda (key, val): _options.add_value(key, val),
	    os.environ.items())
    except KeyError:
	pass

    # Arguments in the '<key>=<value>' style are stripped, and stored in
    # the option database.
    envs = filter(lambda arg: string.find(arg, '=') != -1, args)
    args = filter(lambda arg: string.find(arg, '=') == -1, args)

    def add_option(s):
	[key, value] = string.splitfields(s, '=')
	try:
	    value =  eval(value)
	except:
	    pass
	_options.add_value(key, value)

    try:
	# Add options to option database
	map(lambda s, f=add_option: f(s), envs)
    except ValueError:
	message.error(_program + ': error in variable definition\n')
	message.error('(in one of ' + str(envs) + ')\n')
	message.error('Syntax is "key=name"\n')

    # Ok, finally load options from file in the option "main_options"...
    # Phew...
    is_set, filename = _options.read_value('main', 'options')
    if is_set:
	options.read_options(filename)

    #if not args and not _options.read_value('main', 'all')[1]:
    #	message.error(_program + ': You must pass at least one python file\n')
    #	sys.exit(1)

    # Transform filename arguments to module names.
    args = map(path2module, args)

    return args, formats


def path2module(path):
    "Translates a path to a python module name"
    dirname, basename = os.path.split(path)
    modname, ext = os.path.splitext(basename)
    if ext in PY_EXTS:
	if dirname and dirname not in sys.path:
	    sys.path.insert(0, dirname)
	return modname
    else:
	return path
    
def add_formatter(name, formats):
    try:
	formats.append(formatters.format_init.formatters[name]())
    except KeyError, key:
	message.error("Couldn't find a %s formatter " \
		      "(is it in the formatters subdir?)\n" % key)
	frmtrs = reduce(lambda c, i: c+', '+i,
			formatters.format_init.formatters.keys())
	message.error("Valid formatters are: %s\n" % frmtrs)
	sys.exit(0)

def get_all_modules():
    modules = []
    import sys
    for d in sys.path:
	print "Looking for modules in", d

	try:
	    files = os.listdir(d)
	except os.error:
	    import message
	    message.warning("Couldn't read the directory %s (skipping)" % d)
	    continue

	files = filter(lambda f: os.path.splitext(f)[1] in PY_EXTS, files)
	mods = map(lambda f: os.path.splitext(f)[0], files)

	# Ok, add packages too
	dirs = filter(os.path.isdir, os.listdir(d))
	dirs = filter(ispackage, dirs)

	mods.sort()
	# Remove duplicates
	for mod in mods + dirs:
	    if mod not in modules:
		modules.append(mod)

    return modules

def ispackage(dir):
    return '__init__.py' in os.listdir(dir)

def generate_pages(objs, formats):
    if _options.read_value('main', 'all')[1]:
	objs = get_all_modules()

    objs = map(docobjects.do_import, objs)

    docobjs = []

    for object in objs:
	if object:
	    docobject = docobjects.create_docobject(object)
	    docobjs.append(docobject)

    if _options.read_value('main', 'load')[1]:
	from formatters.SaveFormatter import restore_all
	loadedtrees = restore_all()
    else:
	loadedtrees = []

    trees = []

    for object in docobjs:
	trees.append(object.document(stdmarkup.Markup))

    if _options.read_value('main', 'document')[1]:
	for formatter in formats:
	    for tree in trees:
		formatter.document(tree)

    if _options.read_value('main', 'index')[1]:
	for formatter in formats:
	    for tree in trees + loadedtrees:
		formatter.index(tree)
	    formatter.finish()


PY_EXTS = ('.py', '.pyw', '.pyc', '.pyo')

#
# $Log: pythondoc.py,v $
# Revision 1.3  1999/05/01 00:58:20  daniel
# Fixed log.
#
# 
# *****************  Version 8  *****************
# User: Daniel       Date: 98-12-13   Time: 16:29
# Updated in $/Pythondoc
# New email address.
# Added 'eval' of option values to fix problem with mismatched types.
# Added some more documentation.
# 
# *****************  Version 7  *****************
# User: Daniel       Date: 98-10-06   Time: 22:06
# Updated in $/Pythondoc
# Hmm... the copyright text was printed once for every argument, if
# verbosity
# was turned on...
# 
# *****************  Version 6  *****************
# User: Daniel       Date: 98-10-04   Time: 20:43
# Updated in $/Pythondoc
# Fixed docstring and handling of long options.
# 
# *****************  Version 5  *****************
# User: Daniel       Date: 98-10-04   Time: 19:44
# Updated in $/Pythondoc
# Added new option (-o) to read specific option file.
# Added long option names.
# Allow path names as well as package names on command line.
# 
# *****************  Version 4  *****************
# User: Daniel       Date: 98-08-11   Time: 21:02
# Updated in $/Pythondoc
# - Hides formatters starting with '_'.
# - Added reading of option files.
# - Moved '-v' (verbose) checks before reading option files.
# - Doesn't 'document' loaded trees anymore. They are just passed to the
# indexing stage.
# 
# *****************  Version 3  *****************
# User: Daniel       Date: 98-08-06   Time: 17:24
# Updated in $/Pythondoc
# Added many more options.
# Moved update of docstring to global level.
# Some code rewritten to be able to handle stored doctrees. Still needs
# more work, though.
# 
# *****************  Version 2  *****************
# User: Daniel       Date: 98-07-31   Time: 2:42
# Updated in $/Pythondoc
# Changes for new 'option' module interface.
# - when defining a new option, a type has to be given (to enable
# creating a
#   GUI later).
#  
# Handling of formatters changed. Instead of the former event based
# interface, formatters must take care of the traversal of the doctree
# themselves.
# 
