forked from ipython/ipython
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of git://github.com/ipython/ipython into fronte…
…nd-logging
- Loading branch information
Showing
59 changed files
with
2,502 additions
and
870 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
# 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, Config | ||
) | ||
|
||
from IPython.utils.traitlets import ( | ||
Unicode, List, Int, Enum, Dict | ||
) | ||
from IPython.utils.text import indent | ||
|
||
#----------------------------------------------------------------------------- | ||
# Descriptions for | ||
#----------------------------------------------------------------------------- | ||
|
||
flag_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 | ||
|
||
alias_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 | ||
flag_description = Unicode(flag_description) | ||
alias_description = Unicode(alias_description) | ||
keyvalue_description = Unicode(keyvalue_description) | ||
|
||
|
||
# A sequence of Configurable subclasses whose config=True attributes will | ||
# be exposed at the command line. | ||
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 alias map for configurables | ||
aliases = Dict(dict(log_level='Application.log_level')) | ||
|
||
# flags for loading Configurables or store_const style flags | ||
# flags are loaded from this dict by '--key' flags | ||
# this must be a dict of two-tuples, the first element being the Config/dict | ||
# and the second being the help string for the flag | ||
flags = 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__) | ||
|
||
# ensure self.flags dict is valid | ||
for key,value in self.flags.iteritems(): | ||
assert len(value) == 2, "Bad flag: %r:%s"%(key,value) | ||
assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value) | ||
assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value) | ||
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_alias_help(self): | ||
"""print the alias part of the help""" | ||
if not self.aliases: | ||
return | ||
|
||
print "Aliases" | ||
print "-------" | ||
print self.alias_description | ||
|
||
classdict = {} | ||
for c in self.classes: | ||
classdict[c.__name__] = c | ||
|
||
for alias, longname in self.aliases.iteritems(): | ||
classname, traitname = longname.split('.',1) | ||
cls = classdict[classname] | ||
|
||
trait = cls.class_traits(config=True)[traitname] | ||
help = trait.get_metadata('help') | ||
print alias, "(%s)"%longname, ':', trait.__class__.__name__ | ||
if help: | ||
print indent(help) | ||
|
||
def print_flag_help(self): | ||
"""print the flag part of the help""" | ||
if not self.flags: | ||
return | ||
|
||
print "Flags" | ||
print "-----" | ||
print self.flag_description | ||
|
||
for m, (cfg,help) in self.flags.iteritems(): | ||
print '--'+m | ||
print indent(help) | ||
|
||
def print_help(self): | ||
"""Print the help for each Configurable class in self.classes.""" | ||
self.print_flag_help() | ||
self.print_alias_help() | ||
if self.classes: | ||
print "Class parameters" | ||
print "----------------" | ||
print self.keyvalue_description | ||
|
||
for cls in self.classes: | ||
cls.class_print_help() | ||
|
||
def print_description(self): | ||
"""Print the application description.""" | ||
print self.description | ||
|
||
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, aliases=self.aliases, | ||
flags=self.flags) | ||
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.