Permalink
Browse files

Merge branch 'subcommand' into newapp

  • Loading branch information...
minrk committed May 24, 2011
2 parents 070ea8a + 15bc8a9 commit 8b9e185e58abb9997b680560dbae6bfe92f51031
Showing with 103 additions and 26 deletions.
  1. +83 −14 IPython/config/application.py
  2. +5 −2 IPython/config/loader.py
  3. +3 −3 IPython/core/newapplication.py
  4. +12 −7 docs/examples/core/appconfig.py
@@ -20,16 +20,18 @@
from copy import deepcopy
import logging
+import re
import sys
from IPython.config.configurable import SingletonConfigurable
from IPython.config.loader import (
- KeyValueConfigLoader, PyFileConfigLoader, Config
+ KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
)
from IPython.utils.traitlets import (
- Unicode, List, Int, Enum, Dict
+ Unicode, List, Int, Enum, Dict, Instance
)
+from IPython.utils.importstring import import_item
from IPython.utils.text import indent
#-----------------------------------------------------------------------------
@@ -102,6 +104,14 @@ class Application(SingletonConfigurable):
# and the second being the help string for the flag
flags = Dict()
+ # subcommands for launching other applications
+ # if this is not empty, this will be a parent Application
+ # this must be a dict of two-tuples, the first element being the application class/import string
+ # and the second being the help string for the subcommand
+ subcommands = Dict()
+ # parse_command_line will initialize a subapp, if requested
+ subapp = Instance('IPython.config.application.Application', allow_none=True)
+
def __init__(self, **kwargs):
SingletonConfigurable.__init__(self, **kwargs)
@@ -134,7 +144,23 @@ def init_logging(self):
self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
self._log_handler.setFormatter(self._log_formatter)
self.log.addHandler(self._log_handler)
-
+
+ def initialize(self, argv=None):
+ """Do the basic steps to configure me.
+
+ Override in subclasses.
+ """
+ self.parse_command_line(argv)
+
+
+ def start(self):
+ """Start the app mainloop.
+
+ Override in subclasses.
+ """
+ if self.subapp is not None:
+ return self.subapp.start()
+
def _log_level_changed(self, name, old, new):
"""Adjust the log level when log_level is set."""
self.log.setLevel(new)
@@ -145,13 +171,15 @@ def print_alias_help(self):
return
lines = ['Aliases']
- lines.append('_'*len(lines[0]))
+ lines.append('-'*len(lines[0]))
lines.append(self.alias_description)
lines.append('')
classdict = {}
- for c in self.classes:
- classdict[c.__name__] = c
+ for cls in self.classes:
+ # include all parents (up to, but excluding Configurable) in available names
+ for c in cls.mro()[:-3]:
+ classdict[c.__name__] = c
for alias, longname in self.aliases.iteritems():
classname, traitname = longname.split('.',1)
@@ -161,11 +189,6 @@ def print_alias_help(self):
help = cls.class_get_trait_help(trait)
help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
lines.append(help)
- # header = "%s (%s) : %s"%(alias, longname, trait.__class__.__name__)
- # lines.append(header)
- # help = cls.class_get_trait_help(trait)
- # if help:
- # lines.append(indent(help, flatten=True))
lines.append('')
print '\n'.join(lines)
@@ -175,7 +198,7 @@ def print_flag_help(self):
return
lines = ['Flags']
- lines.append('_'*len(lines[0]))
+ lines.append('-'*len(lines[0]))
lines.append(self.flag_description)
lines.append('')
@@ -185,11 +208,26 @@ def print_flag_help(self):
lines.append('')
print '\n'.join(lines)
+ def print_subcommands(self):
+ """print the subcommand part of the help"""
+ if not self.subcommands:
+ return
+
+ lines = ["Subcommands"]
+ lines.append('-'*len(lines[0]))
+ for subc, (cls,help) in self.subcommands.iteritems():
+ lines.append("%s : %s"%(subc, cls))
+ if help:
+ lines.append(indent(help, flatten=True))
+ lines.append('')
+ print '\n'.join(lines)
+
def print_help(self, classes=False):
"""Print the help for each Configurable class in self.classes.
If classes=False (the default), only flags and aliases are printed
"""
+ self.print_subcommands()
self.print_flag_help()
self.print_alias_help()
@@ -225,11 +263,36 @@ def update_config(self, config):
# Save the combined config as self.config, which triggers the traits
# events.
self.config = newconfig
-
+
+ def initialize_subcommand(self, subc, argv=None):
+ """Initialize a subcommand with argv"""
+ subapp,help = self.subcommands.get(subc, (None,None))
+ if subapp is None:
+ self.print_description()
+ print "No such subcommand: %r"%subc
+ print
+ self.print_subcommands()
+ self.exit(1)
+
+ if isinstance(subapp, basestring):
+ subapp = import_item(subapp)
+
+ # instantiate
+ self.subapp = subapp()
+ # and initialize subapp
+ self.subapp.initialize(argv)
+
def parse_command_line(self, argv=None):
"""Parse the command line arguments."""
argv = sys.argv[1:] if argv is None else argv
+ if self.subcommands and len(argv) > 0:
+ # we have subcommands, and one may have been specified
+ subc, subargv = argv[0], argv[1:]
+ if re.match(r'^\w(\-?\w)*$', subc):
+ # it's a subcommand, and *not* a flag or class parameter
+ return self.initialize_subcommand(subc, subargv)
+
if '-h' in argv or '--help' in argv or '--help-all' in argv:
self.print_description()
self.print_help('--help-all' in argv)
@@ -241,7 +304,13 @@ def parse_command_line(self, argv=None):
loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
flags=self.flags)
- config = loader.load_config()
+ try:
+ config = loader.load_config()
+ except ArgumentError as e:
+ self.log.fatal(str(e))
+ self.print_description()
+ self.print_help()
+ self.exit(1)
self.update_config(config)
def load_config_file(self, filename, path=None):
View
@@ -36,6 +36,9 @@ class ConfigError(Exception):
class ConfigLoaderError(ConfigError):
pass
+class ArgumentError(ConfigLoaderError):
+ pass
+
#-----------------------------------------------------------------------------
# Argparse fix
#-----------------------------------------------------------------------------
@@ -403,14 +406,14 @@ def load_config(self, argv=None, aliases=None, flags=None):
m = item[2:]
cfg,_ = flags.get(m, (None,None))
if cfg is None:
- raise ValueError("Unrecognized flag: %r"%item)
+ raise ArgumentError("Unrecognized flag: %r"%item)
elif isinstance(cfg, (dict, Config)):
# update self.config with Config:
self.config.update(cfg)
else:
raise ValueError("Invalid flag: %r"%flag)
else:
- raise ValueError("Invalid argument: %r"%item)
+ raise ArgumentError("Invalid argument: %r"%item)
return self.config
class ArgParseConfigLoader(CommandLineConfigLoader):
@@ -34,7 +34,7 @@
from IPython.config.application import Application
from IPython.core import release, crashhandler
from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
-from IPython.utils.traitlets import Tuple, Unicode, Type
+from IPython.utils.traitlets import List, Unicode, Type
#-----------------------------------------------------------------------------
# Classes and functions
@@ -56,9 +56,9 @@ class BaseIPythonApplication(Application):
os.path.join(get_ipython_package_dir(), u'config', u'profile')
)
- config_file_paths = Tuple(Unicode, Unicode, Unicode)
+ config_file_paths = List(Unicode)
def _config_file_paths_default(self):
- return (os.getcwdu(), self.ipython_dir, self.builtin_profile_dir)
+ return [os.getcwdu(), self.ipython_dir, self.builtin_profile_dir]
profile_name = Unicode(u'', config=True,
help="""The IPython profile to use."""
@@ -79,17 +79,22 @@ def init_bar(self):
# Pass config to other classes for them to inherit the config.
self.bar = Bar(config=self.config)
+ def initialize(self, argv=None):
+ self.parse_command_line(argv)
+ if self.config_file:
+ self.load_config_file(self.config_file)
+ self.init_foo()
+ self.init_bar()
+
+ def start(self):
+ print "app.config:"
+ print self.config
def main():
app = MyApp()
- app.parse_command_line()
- if app.config_file:
- app.load_config_file(app.config_file)
- app.init_foo()
- app.init_bar()
- print "app.config:"
- print app.config
+ app.initialize()
+ app.start()
if __name__ == "__main__":

0 comments on commit 8b9e185

Please sign in to comment.