From bfa532ae7efd9ac91e0457da911470991886d9c1 2009-08-11 23:54:31 From: Brian Granger Date: 2009-08-11 23:54:31 Subject: [PATCH] Work on Application and loader testing. --- diff --git a/IPython/config/tests/test_loader.py b/IPython/config/tests/test_loader.py new file mode 100644 index 0000000..289550a --- /dev/null +++ b/IPython/config/tests/test_loader.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Tests for IPython.config.loader + +Authors: + +* Brian Granger +* Fernando Perez (design help) +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 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 +#----------------------------------------------------------------------------- + +import os +from tempfile import mkstemp +from unittest import TestCase + +from IPython.config.loader import PyFileConfigLoader, ArgParseConfigLoader + +#----------------------------------------------------------------------------- +# Actual tests +#----------------------------------------------------------------------------- + + +pyfile = """ +A = 10 +B = range(10) +C = True +D = 'hi there' +""" + +class TestPyFileCL(TestCase): + + def test_basic(self): + fd, fname = mkstemp() + f = os.fdopen(fd, 'w') + f.write(pyfile) + f.close() + cl = PyFileConfigLoader(fname) + config = cl.load_config() + self.assertEquals(config.A, 10) + self.assertEquals(config.B, range(10)) + self.assertEquals(config.C, True) + self.assertEquals(config.D, 'hi there') + + +class TestArgParseCL(TestCase): + + def test_basic(self): + + class MyLoader(ArgParseConfigLoader): + arguments = [ + (('-f','--foo'), dict(dest='FOO', type=str)), + (('-b',), dict(dest='BAR', type=int)), + (('-n',), dict(dest='N', action='store_true')), + (('BAM',), dict(type=str)) + ] + + cl = MyLoader() + config = cl.load_config('-f hi -b 10 -n wow'.split()) + self.assertEquals(config.FOO, 'hi') + self.assertEquals(config.BAR, 10) + self.assertEquals(config.N, True) + self.assertEquals(config.BAM, 'wow') + + def test_add_arguments(self): + + class MyLoader(ArgParseConfigLoader): + def _add_arguments(self): + subparsers = self.parser.add_subparsers(dest='subparser_name') + subparser1 = subparsers.add_parser('1') + subparser1.add_argument('-x') + subparser2 = subparsers.add_parser('2') + subparser2.add_argument('y') + + cl = MyLoader() + config = cl.load_config('2 frobble'.split()) + self.assertEquals(config.subparser_name, '2') + self.assertEquals(config.y, 'frobble') + config = cl.load_config('1 -x frobble'.split()) + self.assertEquals(config.subparser_name, '1') + self.assertEquals(config.x, 'frobble') + diff --git a/IPython/core/application.py b/IPython/core/application.py index 9a90a94..0d08a3d 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -30,17 +30,19 @@ There are number of ways in which these directories are used: Questions: -1. Can we limit ourselves to 1 config file or do we want to have a sequence - of them like IPYTHONDIR->RUNTIMEDIR->CWD? -2. Do we need a debug mode that has custom exception handling and can drop - into pdb upno startup? -3. Do we need to use an OutputTrap to capture output and then present it - to a user if startup fails? -4. Do we want the location of the config file(s) to be independent of the - ipython/runtime dir or coupled to it. In other words, can the user select - a config file that is outside their runtime/ipython dir. One model is - that we could have a very strict model of IPYTHONDIR=runtimed dir= - dir used for all config. + +* Can we limit ourselves to 1 config file or do we want to have a sequence + of them like IPYTHONDIR->RUNTIMEDIR->CWD? +* Do we need a debug mode that has custom exception handling and can drop + into pdb upno startup? +* Do we need to use an OutputTrap to capture output and then present it + to a user if startup fails? +* Do we want the location of the config file(s) to be independent of the + ipython/runtime dir or coupled to it. In other words, can the user select + a config file that is outside their runtime/ipython dir. One model is + that we could have a very strict model of IPYTHONDIR=runtimed dir= + dir used for all config. +* Do we install default config files or not? """ #----------------------------------------------------------------------------- @@ -78,7 +80,27 @@ class Application(object): def __init__(self): pass - def find_runtime_dir(self): + def start(self): + """Start the application.""" + self.attempt(self.create_command_line_config) + self.attempt(self.find_runtime_dirs) + self.attempt(self.create_runtime_dirs) + self.attempt(self.find_config_files) + self.attempt(self.create_file_configs) + self.attempt(self.merge_configs) + self.attempt(self.construct) + self.attempt(self.start_logging) + self.attempt(self.start_app) + + #------------------------------------------------------------------------- + # Various stages of Application creation + #------------------------------------------------------------------------- + + def create_command_line_config(self): + """Read the command line args and return its config object.""" + self.command_line_config = Struct() + + def find_runtime_dirs(self): """Find the runtime directory for this application. This should set self.runtime_dir. @@ -86,63 +108,22 @@ class Application(object): pass def create_runtime_dirs(self): - """Create the runtime dirs if they dont exist.""" + """Create the runtime dirs if they don't exist.""" pass - def find_config_file(self): + def find_config_files(self): """Find the config file for this application.""" pass - def create_config(self): - self.config = deepcopy(self.default_config) - - self.pre_file_config() - self.file_config = self.create_file_config() - self.post_file_config() - - self.pre_command_line_config() - self.command_line_config = create_command_line_config() - self.post_command_line_config() + def create_file_configs(self): + self.file_configs = [Struct()] - master_config = self.merge_configs(config, file_config, cl_config) - self.master_config = master_config - return master_config - - def pre_file_config(self): - pass - - def create_file_config(self): - """Read the config file and return its config object.""" - return Struct() - - def post_file_config(self): - pass - - def pre_command_line_config(self): - pass - - def create_command_line_config(self): - """Read the command line args and return its config object.""" - return Struct() - - def post_command_line_config(self): - pass - - def merge_configs(self, config, file_config, cl_config): - config.update(file_config) - config.update(cl_config) - return config - - def start(self): - """Start the application.""" - self.attempt(self.find_runtime_dir) - self.attempt(self.find_runtime_dir) - self.attempt(self.create_runtime_dirs) - self.attempt(self.find_config_file) - self.attempt(self.create_config) - self.attempt(self.construct) - self.attempt(self.start_logging) - self.attempt(self.start_app) + def merge_configs(self): + config = Struct() + all_configs = self.file_configs + self.command_line_config + for c in all_configs: + config.update(c) + self.master_config = config def construct(self, config): """Construct the main components that make up this app.""" @@ -156,6 +137,10 @@ class Application(object): """Actually start the app.""" pass + #------------------------------------------------------------------------- + # Utility methods + #------------------------------------------------------------------------- + def abort(self): """Abort the starting of the application.""" print "Aborting application: ", self.name