annotate configuration/configuration2.py @ 141:c6aea14a3e2b

wip
author Jeff Hammel <k0scist@gmail.com>
date Mon, 01 Dec 2014 18:05:50 -0800
parents 372315b3bb8e
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
140
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
1 #!/usr/bin/env python
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
2
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
3 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
4 unified configuration with serialization/deserialization
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
5 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
6
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
7 # imports
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
8 import argparse
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
9 import copy
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
10 import os
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
11 import sys
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
12
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
13 # imports for configuration providers
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
14 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
15 import json
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
16 except ImportError:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
17 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
18 import simplejson as json
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
19 except ImportError:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
20 json = None
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
21 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
22 import yaml
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
23 except ImportError:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
24 yaml = None
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
25
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
26 # module contents
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
27 __all__ = ['Configuration',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
28 'configuration_providers',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
29 'types',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
30 'UnknownOptionException',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
31 'MissingValueException',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
32 'ConfigurationProviderException',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
33 'TypeCastException',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
34 ]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
35
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
36
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
37
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
38 ### exceptions
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
39
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
40 class UnknownOptionException(Exception):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
41 """exception raised when a non-configuration value is present in the configuration"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
42
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
43 class MissingValueException(Exception):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
44 """exception raised when a required value is missing"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
45
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
46 class ConfigurationProviderException(Exception):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
47 """exception raised when a configuration provider is missing, etc"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
48
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
49 class TypeCastException(Exception):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
50 """exception raised when a configuration item cannot be coerced to a type"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
51
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
52
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
53 ### configuration providers for serialization/deserialization
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
54
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
55 configuration_providers = []
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
56
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
57 class ConfigurationProvider(object):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
58 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
59 abstract base class for configuration providers for
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
60 serialization/deserialization
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
61 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
62 def read(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
63 raise NotImplementedError("Abstract base class")
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
64
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
65 def write(self, config, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
66 if isinstance(filename, basestring):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
67 f = file(filename, 'w')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
68 newfile = True
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
69 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
70 f = filename
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
71 newfile = False
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
72 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
73 self._write(f, config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
74 finally:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
75 if newfile:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
76 f.close()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
77 def _write(self, fp, config):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
78 raise NotImplementedError("Abstract base class")
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
79
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
80 if json:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
81 class JSON(ConfigurationProvider):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
82 indent = 2
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
83 extensions = ['json']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
84 def read(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
85 return json.loads(file(filename).read())
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
86 def _write(self, fp, config):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
87 fp.write(json.dumps(config, indent=self.indent, sort_keys=True))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
88 configuration_providers.append(JSON())
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
89
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
90 if yaml:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
91 class YAML(ConfigurationProvider):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
92 extensions = ['yml', 'yaml']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
93 dump_args = {'default_flow_style': False}
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
94 def read(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
95 f = file(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
96 config = yaml.load(f)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
97 f.close()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
98 return config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
99 def _write(self, fp, config):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
100 fp.write(yaml.dump(config, **self.dump_args))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
101 # TODO: could use templates to get order down, etc
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
102
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
103 configuration_providers.append(YAML())
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
104
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
105 # TODO: add configuration providers
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
106 # - for taking command-line arguments from a file
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
107 # - for .ini files
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
108
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
109 __all__.extend([i.__class__.__name__ for i in configuration_providers])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
110
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
111
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
112 ### optparse interface
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
113
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
114 #class ConfigurationOption(optparse.Option):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
115 # """option that keeps track if it is seen"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
116 # # TODO: this should be configurable or something
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
117 # def take_action(self, action, dest, opt, value, values, parser):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
118 #
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
119 # # switch on types
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
120 # formatter = getattr(parser, 'cli_formatter')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
121 # if formatter:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
122 # formatter = formatter(dest)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
123 # if formatter:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
124 # value = formatter(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
125
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
126 # call the optparse front-end
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
127 # optparse.Option.take_action(self, action, dest, opt, value, values, parser)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
128
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
129 # # add the parsed option to the set of things parsed
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
130 # if not hasattr(parser, 'parsed'):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
131 # parser.parsed = dict()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
132 # parser.parsed[dest] = value
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
133
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
134
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
135 ### plugins for option types
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
136
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
137 class BaseCLI(object):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
138 """base_cli for all option types"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
139
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
140 def __call__(self, name, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
141 """return args, kwargs needed to instantiate an optparse option"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
142
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
143 args = value.get('flags', ['--%s' % name])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
144 if not args:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
145 # No CLI interface
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
146 return (), {}
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
147
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
148 kw = {'dest': name}
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
149 help = value.get('help', name)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
150 if 'default' in value:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
151 kw['default'] = value['default']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
152 help += ' [DEFAULT: %(default)s]'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
153 kw['help'] = help
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
154 kw['action'] = 'store'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
155 return args, kw
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
156
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
157 def take_action(self, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
158 return value
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
159
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
160
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
161 class BoolCLI(BaseCLI):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
162
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
163 def __call__(self, name, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
164
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
165 # preserve the default values
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
166 help = value.get('help')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
167 flags = value.get('flags')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
168
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
169 args, kw = BaseCLI.__call__(self, name, value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
170 kw['help'] = help # reset
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
171 if value.get('default'):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
172 kw['action'] = 'store_false'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
173 if not flags:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
174 args = ['--no-%s' % name]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
175 if not help:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
176 kw['help'] = 'disable {}'.format(name)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
177 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
178 kw['action'] = 'store_true'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
179 if not help:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
180 kw['help'] = 'enable {}'.format(name)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
181 return args, kw
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
182
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
183 class IntCLI(BaseCLI):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
184
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
185 def __call__(self, name, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
186 pass # TODO
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
187
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
188 class ListCLI(BaseCLI):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
189
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
190 def __call__(self, name, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
191 args, kw = BaseCLI.__call__(self, name, value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
192 kw['nargs'] = '+'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
193 return args, kw
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
194
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
195
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
196 class DictCLI(ListCLI):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
197
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
198 delimeter = '='
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
199
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
200 def __call__(self, name, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
201
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
202 # optparse can't handle dict types OOTB
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
203 default = value.get('default')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
204 if isinstance(default, dict):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
205 value = copy.deepcopy(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
206 value['default'] = default.items()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
207
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
208 return ListCLI.__call__(self, name, value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
209
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
210 def take_action(self, value):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
211 if self.delimeter not in value:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
212 raise AssertionError("Each value must be delimited by '%s': %s" % (self.delimeter, value))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
213 return value.split(self.delimeter, 1)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
214
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
215 # types of CLI arguments
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
216 types = {bool: BoolCLI(),
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
217 list: ListCLI(),
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
218 dict: DictCLI(),
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
219 str: BaseCLI(),
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
220 None: BaseCLI()} # default
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
221
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
222 __all__ += [i.__class__.__name__ for i in types.values()]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
223
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
224
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
225 class Configuration(argparse.ArgumentParser):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
226 """declarative configuration object"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
227
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
228 options = {} # configuration basis definition
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
229 extend = set() # which dicts/lists should be extended
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
230 load_option = 'load' # where to put the load option
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
231 load_help = "load configuration from a file"
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
232
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
233 @classmethod
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
234 def parse(cls, args, *_args, **_kwargs):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
235 """get the resultant config dictionary in a single call"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
236 conf = cls(*_args, **_kwargs)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
237 conf.parse_args(*_args, **_kwargs)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
238 return conf.config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
239
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
240 def __init__(self,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
241 configuration_providers=configuration_providers,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
242 types=types,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
243 load=None,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
244 dump='--dump',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
245 **parser_args):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
246
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
247 # sanity check
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
248 if isinstance(self.options, dict):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
249 self.option_dict = self.options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
250 elif isinstance(self.options, (list, tuple)):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
251 self.option_dict = dict(self.options)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
252 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
253 raise NotImplementedError("Configuration: `options` should be castable to a dict")
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
254
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
255 # setup configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
256 self.config = {}
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
257 self.configuration_providers = configuration_providers
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
258 self.types = types
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
259 self.added = set() # set of items added to the configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
260
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
261
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
262 if 'description' not in parser_args:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
263 parser_args['description'] = getattr(self, '__doc__', '')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
264 # if 'formatter' not in parser_args:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
265 # class PlainDescriptionFormatter(optparse.IndentedHelpFormatter):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
266 # """description formatter for console script entry point"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
267 # def format_description(self, description):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
268 # if description:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
269 # return description.strip() + '\n'
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
270 # else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
271 # return ''
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
272 # parser_args['formatter'] = PlainDescriptionFormatter()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
273 # parser_args.setdefault('option_class', ConfigurationOption)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
274
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
275 # setup commandline parser
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
276 argparse.ArgumentParser.__init__(self, **parser_args)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
277 self.parsed = dict()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
278 self.add_arguments(self)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
279
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
280 # add option(s) for configuration_providers
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
281 if load:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
282 self.add_argument(load,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
283 dest=self.load_option,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
284 nargs='+',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
285 help=self.load_help)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
286 elif load is None:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
287 self.add_argument(self.load_option, nargs='*',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
288 help=self.load_help)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
289
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
290 # add an option for dumping
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
291 formats = self.formats()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
292 if formats and dump:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
293 if isinstance(dump, basestring):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
294 dump = [dump]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
295 dump = list(dump)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
296 self.add_argument(*dump,
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
297 **dict(dest='dump',
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
298 help="Output configuration file; Formats: {}".format(formats)))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
299
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
300
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
301 ### iteration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
302
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
303 def items(self):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
304 """items in options"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
305 # TODO: make the class a real iterator
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
306
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
307 # allow options to be a list of 2-tuples
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
308 if isinstance(self.options, dict):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
309 return self.options.items()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
310 return self.options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
311
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
312 ### methods for validating configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
313
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
314 def check(self, config):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
315 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
316 check validity of configuration to be added
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
317 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
318
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
319 # ensure options in configuration are in self.options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
320 unknown_options = [i for i in config if i not in self.option_dict]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
321 if unknown_options:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
322 raise UnknownOptionException("Unknown options: {}".format(', '.join(unknown_options)))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
323
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
324 # ensure options are of the right type (if specified)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
325 for key, value in config.items():
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
326 _type = self.option_dict[key].get('type')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
327 if _type is None and 'default' in self.option_dict[key]:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
328 _type = type(self.option_dict[key]['default'])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
329 if _type is not None:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
330 tocast = True
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
331 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
332 if isinstance(value, _type):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
333 tocast = False
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
334 except TypeError:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
335 # type is a type-casting function, not a proper type
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
336 pass
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
337 if tocast:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
338 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
339 config[key] = _type(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
340 except BaseException, e:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
341 raise TypeCastException("Could not coerce '%s'=%s, to type %s: %s" % (key, value, _type.__name__, e))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
342
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
343 return config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
344
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
345 def validate(self):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
346 """validate resultant configuration"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
347
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
348 for key, value in self.items():
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
349 if key not in self.config:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
350 required = value.get('required')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
351 if required:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
352 if isinstance(required, basestring):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
353 required_message = required
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
354 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
355 required_message = "Parameter {} is required but not present".format(key)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
356 # TODO: this should probably raise all missing values vs
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
357 # one by one
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
358 raise MissingValueException(required_message)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
359
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
360
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
361 ### methods for adding configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
362
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
363 def default_config(self):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
364 """configuration defaults"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
365 defaults = {}
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
366 for key, value in self.items():
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
367 if 'default' in value:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
368 defaults[key] = value['default']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
369 return copy.deepcopy(defaults)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
370
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
371 def get(self, key, default=None):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
372 return self.config.get(key, default)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
373
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
374 def __getitem__(self, key):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
375 return self.config[key]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
376
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
377 def __call__(self, *args):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
378 """add items to configuration and check it"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
379
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
380 # start with defaults
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
381 self.config = self.default_config()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
382
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
383 # add the configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
384 for config in args:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
385 self.add(config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
386
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
387 # validate total configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
388 self.validate()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
389
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
390 # return the configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
391 return self.config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
392
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
393 def add(self, config, check=True):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
394 """update configuration: not undoable"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
395
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
396 # check config to be added
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
397 self.check(config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
398
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
399 # add the configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
400 for key, value in config.items():
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
401 value = copy.deepcopy(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
402 if key in self.extend and key in self.config:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
403 type1 = type(self.config[key])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
404 type2 = type(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
405 assert type1 == type2 # XXX hack
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
406 if type1 == dict:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
407 self.config[key].update(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
408 elif type1 == list:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
409 self.config[key].extend(value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
410 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
411 raise NotImplementedError
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
412 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
413 self.config[key] = value
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
414 self.added.add(key)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
415
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
416
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
417 ### methods for argparse
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
418 ### XXX could go in a subclass
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
419
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
420 def help_formatter(self, option):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
421 if option in self.option_dict:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
422 handler = self.types[self.option_type(option)]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
423 return getattr(handler, 'take_action', lambda x: x)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
424
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
425 def option_type(self, name):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
426 """get the type of an option named `name`"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
427
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
428 value = self.option_dict[name]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
429 if 'type' in value:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
430 return value['type']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
431 if 'default' in value:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
432 default = value['default']
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
433 if default is None:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
434 return None # not <type 'NoneType'>
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
435 return type(value['default'])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
436
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
437 def add_arguments(self, parser):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
438 """add argparse arguments to an ArgumentParser instance"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
439
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
440 for key, value in self.items():
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
441 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
442 handler = self.types[self.option_type(key)]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
443 except KeyError:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
444 # if an option can't be coerced to a type
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
445 # we should just not add a CLI handler for it
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
446 continue
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
447 args, kw = handler(key, value)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
448 if not args:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
449 # No CLI interface
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
450 continue
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
451 parser.add_argument(*args, **kw)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
452
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
453
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
454 ### functions for loading configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
455
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
456 def configuration_files(self, options):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
457 """configuration files to read"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
458
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
459 configuration_files = getattr(options, self.load_option, [])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
460 if isinstance(configuration_files, basestring):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
461 configuration_files = [configuration_files]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
462 return configuration_files
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
463
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
464 def load_configuration_file(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
465 """load a configuration file"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
466 return self.deserialize(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
467
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
468 def read_configuration_files(self, options):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
469 """deserialize configuration"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
470
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
471 configuration_files = self.configuration_files(options)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
472
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
473 # ensure all files are present
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
474 missing = [i for i in configuration_files
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
475 if not os.path.exists(i)]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
476 if missing:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
477 self.error("Missing files: {}".format(', '.join(missing)))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
478
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
479 # load configuration files
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
480 config = []
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
481 for f in configuration_files:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
482 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
483 loaded_config = self.load_configuration_file(f)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
484 if loaded_config:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
485 config.append(loaded_config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
486 except BaseException, e:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
487 parser.error(str(e))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
488 return config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
489
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
490 def parse_args(self, *args, **kw):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
491 """"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
492
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
493 # parse command line options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
494 self.parsed = dict()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
495 options = argparse.ArgumentParser.parse_args(self, *args, **kw)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
496
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
497 # get CLI configuration options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
498 cli_config = dict([(key, value) for key, value in options.__dict__.items()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
499 if key in self.option_dict and key in self.parsed])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
500
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
501 # deserialize configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
502 config = self.read_configuration_files(options)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
503 config.append(cli_config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
504
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
505 missingvalues = None
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
506 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
507 # generate configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
508 self(*config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
509 except MissingValueException, missingvalues:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
510 # errors are handled below
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
511 pass
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
512
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
513 # dump configuration
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
514 self.dump(options, missingvalues)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
515
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
516 # update options from config
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
517 options.__dict__.update(self.config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
518
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
519 # return parsed arguments
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
520 return options
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
521
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
522 def dump(self, options, missingvalues):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
523 """dump configuration, if specified"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
524
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
525 if missingvalues:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
526 self.error(str(missingvalues))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
527
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
528 dump = getattr(options, 'dump')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
529 if dump:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
530 # TODO: have a way of specifying format other than filename
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
531 self.serialize(dump)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
532
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
533
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
534 ### serialization/deserialization
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
535
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
536 def formats(self):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
537 """formats for deserialization"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
538 retval = []
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
539 for provider in self.configuration_providers:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
540 if provider.extensions and hasattr(provider, 'write'):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
541 retval.append(provider.extensions[0])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
542 return retval
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
543
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
544 def configuration_provider(self, format):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
545 """configuration provider guess for a given filename"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
546 for provider in self.configuration_providers:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
547 if format in provider.extensions:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
548 return provider
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
549
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
550 def filename2format(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
551 extension = os.path.splitext(filename)[-1]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
552 return extension.lstrip('.') or None
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
553
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
554 def serialize(self, filename, format=None, full=False):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
555 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
556 serialize configuration to a file
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
557 - filename: path of file to serialize to
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
558 - format: format of configuration provider
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
559 - full: whether to serialize non-set optional strings [TODO]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
560 """
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
561 # TODO: allow file object vs file name
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
562
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
563 if not format:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
564 format = self.filename2format(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
565 if not format:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
566 raise Exception('Please specify a format')
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
567 # TODO: more specific exception type
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
568
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
569 provider = self.configuration_provider(format)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
570 if not provider:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
571 raise Exception("Provider not found for format: %s" % format)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
572
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
573 config = copy.deepcopy(self.config)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
574
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
575 provider.write(config, filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
576
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
577 def deserialize(self, filename, format=None):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
578 """load configuration from a file"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
579 # TODO: allow file object vs file name
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
580
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
581 assert os.path.exists(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
582
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
583 # get the format
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
584 format = format or self.filename2format(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
585
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
586 # get the providers in some sensible order
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
587 providers = self.configuration_providers[:]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
588 if format:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
589 providers.sort(key=lambda x: int(format in x.extensions), reverse=True)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
590
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
591 # deserialize the data
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
592 for provider in providers:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
593 try:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
594 return provider.read(filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
595 except:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
596 continue
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
597 else:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
598 raise ConfigurationProviderException("Could not load {}".format(filename))
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
599
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
600
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
601 class UserConfiguration(Configuration):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
602 """`Configuration` class driven by a config file in user-space"""
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
603
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
604 # configuration items to interpolate as paths
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
605 # TODO: integrate with above BaseCLI
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
606 paths = []
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
607
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
608 def __init__(self, config=None, load='--config', **kwargs):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
609
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
610 # default configuration file
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
611 self.config_name = config or '.' + os.path.splitext(sys.argv[0])[0]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
612 self.default_config_file = os.path.join('~', self.config_name)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
613 self.default_config_file_path = os.path.expanduser(self.default_config_file)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
614 if os.path.exists(self.default_config_file_path):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
615 self.load_help += ' [DEFAULT: %s]' % self.default_config_file
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
616
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
617 Configuration.__init__(self, load=load, **kwargs)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
618
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
619 def validate(self):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
620 Configuration.validate(self)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
621 for path in self.paths:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
622 self.config[path] = os.path.expanduser(self.config[path])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
623
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
624 def configuration_files(self, options, args):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
625 configuration_files = Configuration.configuration_files(self, options, args)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
626 if not configuration_files:
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
627 # load user config only if no config provided
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
628 if os.path.exists(self.default_config_file_path):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
629 configuration_files = [self.default_config_file_path]
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
630 return configuration_files
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
631
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
632 def load_configuration_file(self, filename):
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
633 config = Configuration.load_configuration_file(self, filename)
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
634
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
635 # ignore options that we don't care about
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
636 config = dict([(key, value) for key, value in config.items()
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
637 if key in self.option_dict])
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
638
372315b3bb8e stubbing
Jeff Hammel <k0scist@gmail.com>
parents:
diff changeset
639 return config