##// END OF EJS Templates
add descriptions for the various help sections
add descriptions for the various help sections

File last commit:

r3855:04aa5d23
r3855:04aa5d23
Show More
application.py
231 lines | 7.9 KiB | text/x-python | PythonLexer
# encoding: utf-8
"""
A base class for a configurable application.
Authors:
* Brian Granger
"""
#-----------------------------------------------------------------------------
# Copyright (C) 2008-2011 The IPython Development Team
#
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
from copy import deepcopy
import logging
import sys
from IPython.config.configurable import SingletonConfigurable
from IPython.config.loader import (
KeyValueConfigLoader, PyFileConfigLoader
)
from IPython.utils.traitlets import (
Unicode, List, Int, Enum, Dict
)
from IPython.utils.text import indent
#-----------------------------------------------------------------------------
# Descriptions for
#-----------------------------------------------------------------------------
macro_description = """
Flags are command-line arguments passed as '--<flag>'.
These take no parameters, unlike regular key-value arguments.
They are typically used for setting boolean flags, or enabling
modes that involve setting multiple options together.
""".strip() # trim newlines of front and back
shortname_description = """
These are commonly set parameters, given abbreviated aliases for convenience.
They are set in the same `name=value` way as class parameters, where
<name> is replaced by the real parameter for which it is an alias.
""".strip() # trim newlines of front and back
keyvalue_description = """
Parameters are set from command-line arguments of the form:
`Class.trait=value`. Parameters will *never* be prefixed with '-'.
This line is evaluated in Python, so simple expressions are allowed, e.g.
`C.a='range(3)'` For setting C.a=[0,1,2]
""".strip() # trim newlines of front and back
#-----------------------------------------------------------------------------
# Application class
#-----------------------------------------------------------------------------
class Application(SingletonConfigurable):
"""A singleton application with full configuration support."""
# The name of the application, will usually match the name of the command
# line application
app_name = Unicode(u'application')
# The description of the application that is printed at the beginning
# of the help.
description = Unicode(u'This is an application.')
# default section descriptions
macro_description = Unicode(macro_description)
shortname_description = Unicode(shortname_description)
keyvalue_description = Unicode(keyvalue_description)
# A sequence of Configurable subclasses whose config=True attributes will
# be exposed at the command line (shortnames and help).
classes = List([])
# The version string of this application.
version = Unicode(u'0.0')
# The log level for the application
log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
config=True,
help="Set the log level (0,10,20,30,40,50).")
# the shortname map for configurables
shortnames = Dict(dict(log_level='Application.log_level'))
# macros for loading Configurables or store_const style flags
# macros are loaded from this dict by '--key' flags
macros = Dict()
# macro_help dict keys must match macros
macro_help = Dict()
def __init__(self, **kwargs):
SingletonConfigurable.__init__(self, **kwargs)
# Add my class to self.classes so my attributes appear in command line
# options.
self.classes.insert(0, self.__class__)
# check that macro_help has the right keys
# there is probably a better way to do this that doesn't use 2 dicts
keys = set(self.macros.keys())
badkeys = keys.difference_update(set(self.macro_help.keys()))
if badkeys:
raise KeyError("macro %r has no help in `macro_help`!"%badkeys.pop())
self.init_logging()
def init_logging(self):
"""Start logging for this application.
The default is to log to stdout using a StreaHandler. The log level
starts at loggin.WARN, but this can be adjusted by setting the
``log_level`` attribute.
"""
self.log = logging.getLogger(self.__class__.__name__)
self.log.setLevel(self.log_level)
self._log_handler = logging.StreamHandler()
self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
self._log_handler.setFormatter(self._log_formatter)
self.log.addHandler(self._log_handler)
def _log_level_changed(self, name, old, new):
"""Adjust the log level when log_level is set."""
self.log.setLevel(new)
def print_shortname_help(self):
"""print the shortname part of the help"""
if not self.shortnames:
return
print "Aliases"
print "-------"
print self.shortname_description
print
classdict = {}
for c in self.classes:
classdict[c.__name__] = c
for shortname, longname in self.shortnames.iteritems():
classname, traitname = longname.split('.',1)
cls = classdict[classname]
trait = cls.class_traits(config=True)[traitname]
help = trait.get_metadata('help')
print shortname, "(%s)"%longname, ':', trait.__class__.__name__
if help:
print indent(help)
print
def print_macro_help(self):
"""print the the macro part of the help"""
if not self.macros:
return
print "Flags"
print "-----"
print self.macro_description
print
for m, cfg in self.macros.iteritems():
print '--'+m
print indent(self.macro_help[m])
print
def print_help(self):
"""Print the help for each Configurable class in self.classes."""
self.print_macro_help()
self.print_shortname_help()
if self.classes:
print "Class parameters"
print "----------------"
print self.keyvalue_description
print
for cls in self.classes:
cls.class_print_help()
print
def print_description(self):
"""Print the application description."""
print self.description
print
def print_version(self):
"""Print the version string."""
print self.version
def update_config(self, config):
"""Fire the traits events when the config is updated."""
# Save a copy of the current config.
newconfig = deepcopy(self.config)
# Merge the new config into the current one.
newconfig._merge(config)
# Save the combined config as self.config, which triggers the traits
# events.
self.config = config
def parse_command_line(self, argv=None):
"""Parse the command line arguments."""
argv = sys.argv[1:] if argv is None else argv
if '-h' in argv or '--help' in argv:
self.print_description()
self.print_help()
sys.exit(1)
if '--version' in argv:
self.print_version()
sys.exit(1)
loader = KeyValueConfigLoader(argv=argv, shortnames=self.shortnames,
macros=self.macros)
config = loader.load_config()
self.update_config(config)
def load_config_file(self, filename, path=None):
"""Load a .py based config file by filename and path."""
# TODO: this raises IOError if filename does not exist.
loader = PyFileConfigLoader(filename, path=path)
config = loader.load_config()
self.update_config(config)