##// END OF EJS Templates
Added simple tests for IPython.config.loader.
Brian Granger -
Show More
@@ -1,195 +1,198 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """A factory for creating configuration objects.
3 """A factory for creating configuration objects.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import os
17 import os
18 import sys
18 import sys
19
19
20 from IPython.external import argparse
20 from IPython.external import argparse
21 from IPython.utils.ipstruct import Struct
21 from IPython.utils.ipstruct import Struct
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class ConfigLoaderError(Exception):
28 class ConfigLoaderError(Exception):
29 pass
29 pass
30
30
31
31
32 class ConfigLoader(object):
32 class ConfigLoader(object):
33 """A object for loading configurations from just about anywhere.
33 """A object for loading configurations from just about anywhere.
34
34
35 The resulting configuration is packaged as a :class:`Struct`.
35 The resulting configuration is packaged as a :class:`Struct`.
36
36
37 Notes
37 Notes
38 -----
38 -----
39 A :class:`ConfigLoader` does one thing: load a config from a source
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`.
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
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
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
43 default values or merge multiple configs. These things need to be
44 handled elsewhere.
44 handled elsewhere.
45 """
45 """
46
46
47 def __init__(self):
47 def __init__(self):
48 """A base class for config loaders.
48 """A base class for config loaders.
49
49
50 Examples
50 Examples
51 --------
51 --------
52
52
53 >>> cl = ConfigLoader()
53 >>> cl = ConfigLoader()
54 >>> config = cl.load_config()
54 >>> config = cl.load_config()
55 >>> config
55 >>> config
56 {}
56 {}
57 """
57 """
58 self.clear()
58 self.clear()
59
59
60 def clear(self):
60 def clear(self):
61 self.config = Struct()
61 self.config = Struct()
62
62
63 def load_config(self):
63 def load_config(self):
64 """Load a config from somewhere, return a Struct.
64 """Load a config from somewhere, return a Struct.
65
65
66 Usually, this will cause self.config to be set and then returned.
66 Usually, this will cause self.config to be set and then returned.
67 """
67 """
68 return self.config
68 return self.config
69
69
70
70
71 class FileConfigLoader(ConfigLoader):
71 class FileConfigLoader(ConfigLoader):
72 """A base class for file based configurations.
72 """A base class for file based configurations.
73
73
74 As we add more file based config loaders, the common logic should go
74 As we add more file based config loaders, the common logic should go
75 here.
75 here.
76 """
76 """
77 pass
77 pass
78
78
79
79
80 class PyFileConfigLoader(FileConfigLoader):
80 class PyFileConfigLoader(FileConfigLoader):
81 """A config loader for pure python files.
81 """A config loader for pure python files.
82
82
83 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
84 that are all caps. These attribute are added to the config Struct.
84 that are all caps. These attribute are added to the config Struct.
85 """
85 """
86
86
87 def __init__(self, filename, path='.'):
87 def __init__(self, filename, path='.'):
88 """Build a config loader for a filename and path.
88 """Build a config loader for a filename and path.
89
89
90 Parameters
90 Parameters
91 ----------
91 ----------
92 filename : str
92 filename : str
93 The file name of the config file.
93 The file name of the config file.
94 path : str, list, tuple
94 path : str, list, tuple
95 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
96 paths to try in order.
96 paths to try in order.
97 """
97 """
98 super(PyFileConfigLoader, self).__init__()
98 super(PyFileConfigLoader, self).__init__()
99 self.filename = filename
99 self.filename = filename
100 self.path = path
100 self.path = path
101 self.full_filename = ''
101 self.full_filename = ''
102 self.data = None
102 self.data = None
103
103
104 def load_config(self):
104 def load_config(self):
105 """Load the config from a file and return it as a Struct."""
105 """Load the config from a file and return it as a Struct."""
106 self._find_file()
106 self._find_file()
107 self._read_file_as_dict()
107 self._read_file_as_dict()
108 self._convert_to_struct()
108 self._convert_to_struct()
109 return self.config
109 return self.config
110
110
111 def _find_file(self):
111 def _find_file(self):
112 """Try to find the file by searching the paths."""
112 """Try to find the file by searching the paths."""
113 if os.path.isfile(os.path.expanduser(self.filename)):
114 self.full_filename = os.path.expanduser(self.filename)
115 return
113 if self.path == '.':
116 if self.path == '.':
114 self.path = [os.getcwd()]
117 self.path = [os.getcwd()]
115 if not isinstance(path, (list, tuple)):
118 if not isinstance(path, (list, tuple)):
116 raise TypeError("path must be a list or tuple, got: %r" % self.path)
119 raise TypeError("path must be a list or tuple, got: %r" % self.path)
117 for p in self.path:
120 for p in self.path:
118 if p == '.': p = os.getcwd()
121 if p == '.': p = os.getcwd()
119 full_filename = os.path.join(p, self.filename)
122 full_filename = os.path.expanduser(os.path.join(p, self.filename))
120 if os.path.isfile(full_filename):
123 if os.path.isfile(full_filename):
121 self.full_filename = full_filename
124 self.full_filename = full_filename
122 return
125 return
123 raise IOError("Config file does not exist in any "
126 raise IOError("Config file does not exist in any "
124 "of the search paths: %r, %r" % \
127 "of the search paths: %r, %r" % \
125 (self.filename, self.path))
128 (self.filename, self.path))
126
129
127 def _read_file_as_dict(self):
130 def _read_file_as_dict(self):
128 self.data = {}
131 self.data = {}
129 execfile(self.full_filename, self.data)
132 execfile(self.full_filename, self.data)
130
133
131 def _convert_to_struct(self):
134 def _convert_to_struct(self):
132 if self.data is None:
135 if self.data is None:
133 ConfigLoaderError('self.data does not exist')
136 ConfigLoaderError('self.data does not exist')
134 for k, v in self.data.iteritems():
137 for k, v in self.data.iteritems():
135 if k == k.upper():
138 if k == k.upper():
136 self.config[k] = v
139 self.config[k] = v
137
140
138
141
139 class CommandLineConfigLoader(ConfigLoader):
142 class CommandLineConfigLoader(ConfigLoader):
140 """A config loader for command line arguments.
143 """A config loader for command line arguments.
141
144
142 As we add more command line based loaders, the common logic should go
145 As we add more command line based loaders, the common logic should go
143 here.
146 here.
144 """
147 """
145
148
146
149
147 class NoDefault(object): pass
150 class NoDefault(object): pass
148 NoDefault = NoDefault()
151 NoDefault = NoDefault()
149
152
150 class ArgParseConfigLoader(CommandLineConfigLoader):
153 class ArgParseConfigLoader(CommandLineConfigLoader):
151
154
152 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
155 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
153 arguments = []
156 arguments = []
154
157
155 def __init__(self, *args, **kw):
158 def __init__(self, *args, **kw):
156 """Create a config loader for use with argparse.
159 """Create a config loader for use with argparse.
157
160
158 The args and kwargs arguments here are passed onto the constructor
161 The args and kwargs arguments here are passed onto the constructor
159 of :class:`argparse.ArgumentParser`.
162 of :class:`argparse.ArgumentParser`.
160 """
163 """
161 super(CommandLineConfigLoader, self).__init__()
164 super(CommandLineConfigLoader, self).__init__()
162 self.args = args
165 self.args = args
163 self.kw = kw
166 self.kw = kw
164
167
165 def load_config(self, args=None):
168 def load_config(self, args=None):
166 """Parse command line arguments and return as a Struct."""
169 """Parse command line arguments and return as a Struct."""
167 self._create_parser()
170 self._create_parser()
168 self._parse_args(args)
171 self._parse_args(args)
169 self._convert_to_struct()
172 self._convert_to_struct()
170 return self.config
173 return self.config
171
174
172 def _create_parser(self):
175 def _create_parser(self):
173 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
176 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
174 self._add_arguments()
177 self._add_arguments()
175
178
176 def _add_arguments(self):
179 def _add_arguments(self):
177 for argument in self.arguments:
180 for argument in self.arguments:
178 argument[1]['default'] = NoDefault
181 if not argument[1].has_key('default'):
179 print argument
182 argument[1]['default'] = NoDefault
180 self.parser.add_argument(*argument[0],**argument[1])
183 self.parser.add_argument(*argument[0],**argument[1])
181
184
182 def _parse_args(self, args=None):
185 def _parse_args(self, args=None):
183 """self.parser->self.parsed_data"""
186 """self.parser->self.parsed_data"""
184 if args is None:
187 if args is None:
185 self.parsed_data = self.parser.parse_args()
188 self.parsed_data = self.parser.parse_args()
186 else:
189 else:
187 self.parsed_data = self.parser.parse_args(args)
190 self.parsed_data = self.parser.parse_args(args)
188
191
189 def _convert_to_struct(self):
192 def _convert_to_struct(self):
190 """self.parsed_data->self.config"""
193 """self.parsed_data->self.config"""
191 self.config = Struct()
194 self.config = Struct()
192 for k, v in vars(self.parsed_data).items():
195 for k, v in vars(self.parsed_data).items():
193 if v is not NoDefault:
196 if v is not NoDefault:
194 setattr(self.config, k, v)
197 setattr(self.config, k, v)
195
198
General Comments 0
You need to be logged in to leave comments. Login now