##// END OF EJS Templates
Working version of the new config loaders for .py files and argparse.
Brian Granger -
Show More
@@ -0,0 +1,174 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 An application for IPython
5
6 Authors:
7
8 * Brian Granger
9 * Fernando Perez
10
11 Notes
12 -----
13
14 The following directories are relevant in the startup of an app:
15
16 * The ipythondir. This has a default, but can be set by IPYTHONDIR or at
17 the command line.
18 * The current working directory.
19 * Another runtime directory. With some applications (engine, controller) we
20 need the ability to have different cluster configs. Each of these needs
21 to have its own config, security dir and log dir. We could simply treat
22 these as regular ipython dirs.
23
24 There are number of ways in which these directories are used:
25
26 * For config files.
27 * For other assets and resources needed to run. These include
28 plugins, magics, furls files.
29 * For writing various things created at runtime like logs, furl files, etc.
30
31 Questions:
32
33 1. Can we limit ourselves to 1 config file or do we want to have a sequence
34 of them like IPYTHONDIR->RUNTIMEDIR->CWD?
35 2. Do we need a debug mode that has custom exception handling and can drop
36 into pdb upno startup?
37 3. Do we need to use an OutputTrap to capture output and then present it
38 to a user if startup fails?
39 4. Do we want the location of the config file(s) to be independent of the
40 ipython/runtime dir or coupled to it. In other words, can the user select
41 a config file that is outside their runtime/ipython dir. One model is
42 that we could have a very strict model of IPYTHONDIR=runtimed dir=
43 dir used for all config.
44 """
45
46 #-----------------------------------------------------------------------------
47 # Copyright (C) 2008-2009 The IPython Development Team
48 #
49 # Distributed under the terms of the BSD License. The full license is in
50 # the file COPYING, distributed as part of this software.
51 #-----------------------------------------------------------------------------
52
53 #-----------------------------------------------------------------------------
54 # Imports
55 #-----------------------------------------------------------------------------
56
57 import sys
58 from copy import deepcopy
59 from IPython.utils.ipstruct import Struct
60
61 #-----------------------------------------------------------------------------
62 # Classes and functions
63 #-----------------------------------------------------------------------------
64
65
66 class ApplicationError(Exception):
67 pass
68
69
70 class Application(object):
71
72 runtime_dirs = []
73 default_config = Struct()
74 runtime_dir = ''
75 config_file = ''
76 name = ''
77
78 def __init__(self):
79 pass
80
81 def find_runtime_dir(self):
82 """Find the runtime directory for this application.
83
84 This should set self.runtime_dir.
85 """
86 pass
87
88 def create_runtime_dirs(self):
89 """Create the runtime dirs if they dont exist."""
90 pass
91
92 def find_config_file(self):
93 """Find the config file for this application."""
94 pass
95
96 def create_config(self):
97 self.config = deepcopy(self.default_config)
98
99 self.pre_file_config()
100 self.file_config = self.create_file_config()
101 self.post_file_config()
102
103 self.pre_command_line_config()
104 self.command_line_config = create_command_line_config()
105 self.post_command_line_config()
106
107 master_config = self.merge_configs(config, file_config, cl_config)
108 self.master_config = master_config
109 return master_config
110
111 def pre_file_config(self):
112 pass
113
114 def create_file_config(self):
115 """Read the config file and return its config object."""
116 return Struct()
117
118 def post_file_config(self):
119 pass
120
121 def pre_command_line_config(self):
122 pass
123
124 def create_command_line_config(self):
125 """Read the command line args and return its config object."""
126 return Struct()
127
128 def post_command_line_config(self):
129 pass
130
131 def merge_configs(self, config, file_config, cl_config):
132 config.update(file_config)
133 config.update(cl_config)
134 return config
135
136 def start(self):
137 """Start the application."""
138 self.attempt(self.find_runtime_dir)
139 self.attempt(self.find_runtime_dir)
140 self.attempt(self.create_runtime_dirs)
141 self.attempt(self.find_config_file)
142 self.attempt(self.create_config)
143 self.attempt(self.construct)
144 self.attempt(self.start_logging)
145 self.attempt(self.start_app)
146
147 def construct(self, config):
148 """Construct the main components that make up this app."""
149 pass
150
151 def start_logging(self):
152 """Start logging, if needed, at the last possible moment."""
153 pass
154
155 def start_app(self):
156 """Actually start the app."""
157 pass
158
159 def abort(self):
160 """Abort the starting of the application."""
161 print "Aborting application: ", self.name
162 sys.exit(1)
163
164 def attempt(self, func):
165 try:
166 func()
167 except:
168 self.handle_error()
169 self.abort()
170
171 def handle_error(self):
172 print "I am dying!"
173
174 No newline at end of file
@@ -15,7 +15,9 b''
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
17 import os
18 import sys
18
19
20 from IPython.external import argparse
19 from IPython.utils.ipstruct import Struct
21 from IPython.utils.ipstruct import Struct
20
22
21 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
@@ -31,6 +33,15 b' class ConfigLoader(object):'
31 """A object for loading configurations from just about anywhere.
33 """A object for loading configurations from just about anywhere.
32
34
33 The resulting configuration is packaged as a :class:`Struct`.
35 The resulting configuration is packaged as a :class:`Struct`.
36
37 Notes
38 -----
39 A :class:`ConfigLoader` does one thing: load a config from a source
40 (file, command line arguments) and returns the data as a :class:`Struct`.
41 There are lots of things that :class:`ConfigLoader` does not do. It does
42 not implement complex logic for finding config files. It does not handle
43 default values or merge multiple configs. These things need to be
44 handled elsewhere.
34 """
45 """
35
46
36 def __init__(self):
47 def __init__(self):
@@ -58,6 +69,15 b' class ConfigLoader(object):'
58
69
59
70
60 class FileConfigLoader(ConfigLoader):
71 class FileConfigLoader(ConfigLoader):
72 """A base class for file based configurations.
73
74 As we add more file based config loaders, the common logic should go
75 here.
76 """
77 pass
78
79
80 class PyFileConfigLoader(FileConfigLoader):
61 """A config loader for pure python files.
81 """A config loader for pure python files.
62
82
63 This calls execfile on a plain python file and looks for attributes
83 This calls execfile on a plain python file and looks for attributes
@@ -73,114 +93,103 b' class FileConfigLoader(ConfigLoader):'
73 The file name of the config file.
93 The file name of the config file.
74 path : str, list, tuple
94 path : str, list, tuple
75 The path to search for the config file on, or a sequence of
95 The path to search for the config file on, or a sequence of
76 paths to try in order
96 paths to try in order.
77
78 Examples
79 --------
80
81
82 """
97 """
98 super(PyFileConfigLoader, self).__init__()
83 self.filename = filename
99 self.filename = filename
84 self.path = path
100 self.path = path
85 self.full_filename = ''
101 self.full_filename = ''
86 self.data = None
102 self.data = None
87 ConfigLoader.__init__(self)
88
103
89 def find_file(self):
104 def load_config(self):
90 """Implement file finding logic here."""
105 """Load the config from a file and return it as a Struct."""
91 self.full_filename = self.filename
106 self._find_file()
107 self._read_file_as_dict()
108 self._convert_to_struct()
109 return self.config
92
110
93 def read_file_as_dict(self):
111 def _find_file(self):
94 if not os.path.isfile(self.full_filename):
112 """Try to find the file by searching the paths."""
95 raise IOError("config file does not exist: %r" % self.fullfilename)
113 if self.path == '.':
114 self.path = [os.getcwd()]
115 if not isinstance(path, (list, tuple)):
116 raise TypeError("path must be a list or tuple, got: %r" % self.path)
117 for p in self.path:
118 if p == '.': p = os.getcwd()
119 full_filename = os.path.join(p, self.filename)
120 if os.path.isfile(full_filename):
121 self.full_filename = full_filename
122 return
123 raise IOError("Config file does not exist in any "
124 "of the search paths: %r, %r" % \
125 (self.filename, self.path))
126
127 def _read_file_as_dict(self):
96 self.data = {}
128 self.data = {}
97 execfile(self.full_filename, self.data)
129 execfile(self.full_filename, self.data)
98
130
99 def convert_to_struct(self):
131 def _convert_to_struct(self):
100 if self.data is None:
132 if self.data is None:
101 ConfigLoaderError('self.data does not exist')
133 ConfigLoaderError('self.data does not exist')
102 for k, v in self.data.iteritems():
134 for k, v in self.data.iteritems():
103 if k == k.upper():
135 if k == k.upper():
104 self.config[k] = v
136 self.config[k] = v
105
137
106 def load_config(self):
107 self.find_file()
108 self.read_file_as_dict()
109 self.convert_to_struct()
110 return self.config
111
112 class PyConfigLoader(object):
113 pass
114
115
116 class DefaultFileConfigLoader(object):
117
118 def __init__(self, filename, install_location):
119 pass
120
121 def load_config(self):
122 pass
123
124 def install(self, force=False):
125 pass
126
127
138
128 class CommandLineConfigLoader(ConfigLoader):
139 class CommandLineConfigLoader(ConfigLoader):
140 """A config loader for command line arguments.
129
141
130 def __init__(self):
142 As we add more command line based loaders, the common logic should go
131 self.parser = None
143 here.
132 self.parsed_data = None
144 """
133 self.clear()
134
135
136 def clear(self):
137 self.config = Struct()
138
139 def load_config(self, args=None):
140 self.create_parser()
141 self.parse_args(args)
142 self.convert_to_struct()
143 return self.config
144
145 def create_parser(self):
146 """Create self.parser"""
147
148 def parse_args(self, args=None):
149 """self.parser->self.parsed_data"""
150 if self.parser is None:
151 raise ConfigLoaderError('self.parser does not exist')
152 if args is None:
153 self.parsed_data = parser.parse_args()
154 else:
155 self.parse_data = parser.parse_args(args)
156
145
157 def convert_to_struct(self):
158 """self.parsed_data->self.config"""
159 if self.parsed_data is None:
160 raise ConfigLoaderError('self.parsed_data does not exist')
161 self.config = Struct(vars(self.parsed_data))
162
146
147 class NoDefault(object): pass
148 NoDefault = NoDefault()
163
149
164 class ArgParseConfigLoader(CommandLineConfigLoader):
150 class ArgParseConfigLoader(CommandLineConfigLoader):
165
151
166 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
152 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
167 arguments = []
153 arguments = []
168
154
169 def __init__(self, *args, **kw):
155 def __init__(self, *args, **kw):
170 """Create a config loader for use with argparse.
156 """Create a config loader for use with argparse.
171
157
172 The args and kwargs arguments here are passed onto the constructor
158 The args and kwargs arguments here are passed onto the constructor
173 of :class:`argparse.ArgumentParser`.
159 of :class:`argparse.ArgumentParser`.
174 """
160 """
161 super(CommandLineConfigLoader, self).__init__()
175 self.args = args
162 self.args = args
176 self.kw = kw
163 self.kw = kw
177 CommandLineConfigLoader.__init__(self)
178
164
179 def create_parser(self):
165 def load_config(self, args=None):
166 """Parse command line arguments and return as a Struct."""
167 self._create_parser()
168 self._parse_args(args)
169 self._convert_to_struct()
170 return self.config
171
172 def _create_parser(self):
180 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
173 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
181 self.add_arguments()
174 self._add_arguments()
182
175
183 def add_arguments(self):
176 def _add_arguments(self):
184 for argument in self.arguments:
177 for argument in self.arguments:
178 argument[1]['default'] = NoDefault
179 print argument
185 self.parser.add_argument(*argument[0],**argument[1])
180 self.parser.add_argument(*argument[0],**argument[1])
186
181
182 def _parse_args(self, args=None):
183 """self.parser->self.parsed_data"""
184 if args is None:
185 self.parsed_data = self.parser.parse_args()
186 else:
187 self.parsed_data = self.parser.parse_args(args)
188
189 def _convert_to_struct(self):
190 """self.parsed_data->self.config"""
191 self.config = Struct()
192 for k, v in vars(self.parsed_data).items():
193 if v is not NoDefault:
194 setattr(self.config, k, v)
195
General Comments 0
You need to be logged in to leave comments. Login now