Merge lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue
- remove-circular-dependencies
- Merge into trunk
Proposed by
Ricardo Kirkner
Status: | Merged |
---|---|
Approved by: | Ricardo Kirkner |
Approved revision: | 34 |
Merged at revision: | 32 |
Proposed branch: | lp:~ricardokirkner/configglue/remove-circular-dependencies |
Merge into: | lp:configglue |
Diff against target: |
2274 lines (+1088/-1053) 11 files modified
configglue/inischema/glue.py (+7/-3) configglue/pyschema/__init__.py (+2/-264) configglue/pyschema/options.py (+0/-245) configglue/pyschema/parser.py (+15/-6) configglue/pyschema/schema.py (+371/-3) configglue/utils.py (+162/-0) tests/pyschema/test_glue2glue.py (+4/-1) tests/pyschema/test_options.py (+0/-511) tests/pyschema/test_parser.py (+16/-6) tests/pyschema/test_schema.py (+494/-4) tests/pyschema/test_schemaconfig.py (+17/-10) |
To merge this branch: | bzr merge lp:~ricardokirkner/configglue/remove-circular-dependencies |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John Lenton | Approve | ||
Review via email: mp+44167@code.launchpad.net |
Commit message
refactored code to remove circular dependencies
Description of the change
This branch refactors some imports to remove circular dependencies, and cleans up a bit the project structure.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'configglue/inischema/glue.py' |
2 | --- configglue/inischema/glue.py 2010-08-05 11:55:13 +0000 |
3 | +++ configglue/inischema/glue.py 2010-12-19 19:01:37 +0000 |
4 | @@ -19,9 +19,13 @@ |
5 | """ |
6 | from __future__ import absolute_import |
7 | |
8 | -from configglue.pyschema import schemaconfigglue, ini2schema |
9 | - |
10 | -__all__ = ('configglue',) |
11 | +from configglue.utils import schemaconfigglue, ini2schema |
12 | + |
13 | + |
14 | +__all__ = [ |
15 | + 'configglue', |
16 | +] |
17 | + |
18 | |
19 | def configglue(fileobj, *filenames, **kwargs): |
20 | args = kwargs.pop('args', None) |
21 | |
22 | === modified file 'configglue/pyschema/__init__.py' |
23 | --- configglue/pyschema/__init__.py 2010-12-18 21:12:24 +0000 |
24 | +++ configglue/pyschema/__init__.py 2010-12-19 19:01:37 +0000 |
25 | @@ -15,267 +15,5 @@ |
26 | # |
27 | ############################################################################### |
28 | |
29 | -import __builtin__ |
30 | -from optparse import OptionParser |
31 | -from collections import namedtuple |
32 | -import sys |
33 | - |
34 | -# XXX: more imports at bottom (we probably want to fix this) |
35 | - |
36 | -SchemaGlue = namedtuple("SchemaGlue", "schema_parser option_parser options args") |
37 | -IniGlue = namedtuple("IniGlue", " option_parser options args") |
38 | - |
39 | -def ini2schema(fd, p=None): |
40 | - """ |
41 | - Turn a fd that refers to a INI-style schema definition into a |
42 | - SchemaConfigParser object |
43 | - |
44 | - @param fd: file-like object to read the schema from |
45 | - @param p: a parser to use. If not set, uses AttributedConfigParser |
46 | - """ |
47 | - if p is None: |
48 | - p = AttributedConfigParser() |
49 | - p.readfp(fd) |
50 | - p.parse_all() |
51 | - |
52 | - parser2option = {'unicode': options.StringConfigOption, |
53 | - 'int': options.IntConfigOption, |
54 | - 'bool': options.BoolConfigOption, |
55 | - 'lines': options.LinesConfigOption} |
56 | - |
57 | - class MySchema(Schema): |
58 | - pass |
59 | - |
60 | - for section_name in p.sections(): |
61 | - if section_name == '__main__': |
62 | - section = MySchema |
63 | - else: |
64 | - section = ConfigSection() |
65 | - setattr(MySchema, section_name, section) |
66 | - for option_name in p.options(section_name): |
67 | - option = p.get(section_name, option_name) |
68 | - |
69 | - parser = option.attrs.pop('parser', 'unicode') |
70 | - parser_args = option.attrs.pop('parser.args', '').split() |
71 | - parser_fun = getattr(parsers, parser, None) |
72 | - if parser_fun is None: |
73 | - parser_fun = getattr(__builtin__, parser, None) |
74 | - if parser_fun is None: |
75 | - parser_fun = lambda x: x |
76 | - |
77 | - attrs = {} |
78 | - option_help = option.attrs.pop('help', None) |
79 | - if option_help is not None: |
80 | - attrs['help'] = option_help |
81 | - if not option.is_empty: |
82 | - attrs['default'] = parser_fun(option.value, *parser_args) |
83 | - option_action = option.attrs.pop('action', None) |
84 | - if option_action is not None: |
85 | - attrs['action'] = option_action |
86 | - |
87 | - klass = parser2option.get(parser, options.StringConfigOption) |
88 | - if parser == 'lines': |
89 | - instance = klass(options.StringConfigOption(), **attrs) |
90 | - else: |
91 | - instance = klass(**attrs) |
92 | - setattr(section, option_name, instance) |
93 | - |
94 | - return SchemaConfigParser(MySchema()) |
95 | - |
96 | - |
97 | -def schemaconfigglue(parser, op=None, argv=None): |
98 | - """Populate an OptionParser with options and defaults taken from a |
99 | - fully loaded SchemaConfigParser. |
100 | - """ |
101 | - |
102 | - def long_name(option): |
103 | - if option.section.name == '__main__': |
104 | - return option.name |
105 | - return option.section.name + '_' + option.name |
106 | - |
107 | - def opt_name(option): |
108 | - return long_name(option).replace('-', '_') |
109 | - |
110 | - if op is None: |
111 | - op = OptionParser() |
112 | - if argv is None: |
113 | - argv = sys.argv[1:] |
114 | - schema = parser.schema |
115 | - |
116 | - for section in schema.sections(): |
117 | - if section.name == '__main__': |
118 | - og = op |
119 | - else: |
120 | - og = op.add_option_group(section.name) |
121 | - for option in section.options(): |
122 | - kwargs = {} |
123 | - if option.help: |
124 | - kwargs['help'] = option.help |
125 | - kwargs['default'] = parser.get(section.name, option.name) |
126 | - kwargs['action'] = option.action |
127 | - og.add_option('--' + long_name(option), **kwargs) |
128 | - options, args = op.parse_args(argv) |
129 | - |
130 | - for section in schema.sections(): |
131 | - for option in section.options(): |
132 | - value = getattr(options, opt_name(option)) |
133 | - if parser.get(section.name, option.name) != value: |
134 | - # the value has been overridden by an argument; |
135 | - # update it. |
136 | - parser.set(section.name, option.name, value) |
137 | - |
138 | - return IniGlue(op, options, args) |
139 | - |
140 | -def super_vars(obj): |
141 | - """An extended version of vars() that walks all base classes.""" |
142 | - items = {} |
143 | - if hasattr(obj, '__mro__'): |
144 | - bases = map(vars, obj.__mro__) |
145 | - map(items.update, bases) |
146 | - else: |
147 | - items = vars(obj) |
148 | - return items |
149 | - |
150 | -NO_DEFAULT = object() |
151 | - |
152 | - |
153 | -class ConfigOption(object): |
154 | - """Base class for Config Options. |
155 | - |
156 | - ConfigOptions are never bound to a particular conguration file, and |
157 | - simply describe one particular available option. |
158 | - |
159 | - They also know how to parse() the content of a config file in to the right |
160 | - type of object. |
161 | - |
162 | - If self.raw == True, then variable interpolation will not be carried out |
163 | - for this config option. |
164 | - |
165 | - If self.require_parser == True, then the parse() method will have a second |
166 | - argument, parser, that should receive the whole SchemaConfigParser to |
167 | - do the parsing. This is needed for config options that need to look at |
168 | - other parts of the config file to be able to carry out their parsing, |
169 | - like DictConfigOptions. |
170 | - |
171 | - If self.fatal == True, SchemaConfigParser's parse_all will raise an |
172 | - exception if no value for this option is provided in the configuration |
173 | - file. Otherwise, the self.default value will be used if the option is |
174 | - omitted. |
175 | - |
176 | - In runtime, after instantiating the Schema, each ConfigOption will also |
177 | - know its own name and to which section it belongs. |
178 | - """ |
179 | - |
180 | - require_parser = False |
181 | - |
182 | - def __init__(self, name='', raw=False, default=NO_DEFAULT, fatal=False, help='', |
183 | - section=None, action='store'): |
184 | - self.name = name |
185 | - self.raw = raw |
186 | - self.fatal = fatal |
187 | - if default is NO_DEFAULT: |
188 | - default = self._get_default() |
189 | - self.default = default |
190 | - self.help = help |
191 | - self.section = section |
192 | - self.action = action |
193 | - |
194 | - def __eq__(self, other): |
195 | - try: |
196 | - equal = (self.name == other.name and |
197 | - self.raw == other.raw and |
198 | - self.fatal == other.fatal and |
199 | - self.default == other.default and |
200 | - self.help == other.help) |
201 | - if self.section is not None and other.section is not None: |
202 | - # only test for section name to avoid recursion |
203 | - equal &= self.section.name == other.section.name |
204 | - else: |
205 | - equal &= (self.section is None and other.section is None) |
206 | - except AttributeError: |
207 | - equal = False |
208 | - |
209 | - return equal |
210 | - |
211 | - def __repr__(self): |
212 | - extra = ' raw' if self.raw else '' |
213 | - extra += ' fatal' if self.fatal else '' |
214 | - section = self.section.name if self.section is not None else None |
215 | - if section is not None: |
216 | - name = " %s.%s" % (section, self.name) |
217 | - elif self.name: |
218 | - name = " %s" % self.name |
219 | - else: |
220 | - name = '' |
221 | - value = "<ConfigOption%s%s>" % (name, extra) |
222 | - return value |
223 | - |
224 | - def _get_default(self): |
225 | - return None |
226 | - |
227 | - def parse(self, value): |
228 | - raise NotImplementedError() |
229 | - |
230 | - |
231 | -class ConfigSection(object): |
232 | - """A group of options. |
233 | - |
234 | - This class is just a bag you can dump ConfigOptions in. |
235 | - |
236 | - After instantiating the Schema, each ConfigSection will know its own |
237 | - name. |
238 | - """ |
239 | - def __init__(self, name=''): |
240 | - self.name = name |
241 | - |
242 | - def __eq__(self, other): |
243 | - return (self.name == other.name and |
244 | - self.options() == other.options()) |
245 | - |
246 | - def __repr__(self): |
247 | - if self.name: |
248 | - name = " %s" % self.name |
249 | - else: |
250 | - name = '' |
251 | - value = "<ConfigSection%s>" % name |
252 | - return value |
253 | - |
254 | - def has_option(self, name): |
255 | - """Return True if a ConfigOption with the given name is available""" |
256 | - opt = getattr(self, name, None) |
257 | - return isinstance(opt, ConfigOption) |
258 | - |
259 | - def option(self, name): |
260 | - """Return a ConfigOption by name""" |
261 | - opt = getattr(self, name, None) |
262 | - assert opt is not None, "Invalid ConfigOption name '%s'" % name |
263 | - return opt |
264 | - |
265 | - def options(self): |
266 | - """Return a list of all available ConfigOptions within this section""" |
267 | - return [getattr(self, att) for att in vars(self) |
268 | - if isinstance(getattr(self, att), ConfigOption)] |
269 | - |
270 | - |
271 | -# usability tweak -- put everything in the base namespace to make import lines |
272 | -# shorter |
273 | -from options import (BoolConfigOption, DictConfigOption, IntConfigOption, |
274 | - LinesConfigOption, StringConfigOption, TupleConfigOption) |
275 | -from parser import SchemaConfigParser |
276 | -from schema import Schema |
277 | - |
278 | -def configglue(schema_class, configs, usage=None): |
279 | - scp = SchemaConfigParser(schema_class()) |
280 | - scp.read(configs) |
281 | - if usage is not None: |
282 | - op = OptionParser(usage=usage) |
283 | - else: |
284 | - op = None |
285 | - parser, opts, args = schemaconfigglue(scp, op=op) |
286 | - is_valid, reasons = scp.is_valid(report=True) |
287 | - if not is_valid: |
288 | - parser.error(reasons[0]) |
289 | - return SchemaGlue(scp, parser, opts, args) |
290 | - |
291 | -# circular import avoidance |
292 | -from configglue.inischema import AttributedConfigParser, parsers |
293 | +from configglue.pyschema.parser import * |
294 | +from configglue.pyschema.schema import * |
295 | |
296 | === removed file 'configglue/pyschema/options.py' |
297 | --- configglue/pyschema/options.py 2010-08-04 21:00:09 +0000 |
298 | +++ configglue/pyschema/options.py 1970-01-01 00:00:00 +0000 |
299 | @@ -1,245 +0,0 @@ |
300 | -############################################################################### |
301 | -# |
302 | -# configglue -- glue for your apps' configuration |
303 | -# |
304 | -# A library for simple, DRY configuration of applications |
305 | -# |
306 | -# (C) 2009--2010 by Canonical Ltd. |
307 | -# originally by John R. Lenton <john.lenton@canonical.com> |
308 | -# incorporating schemaconfig as configglue.pyschema |
309 | -# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@canonical.com> |
310 | -# |
311 | -# Released under the BSD License (see the file LICENSE) |
312 | -# |
313 | -# For bug reports, support, and new releases: http://launchpad.net/configglue |
314 | -# |
315 | -############################################################################### |
316 | - |
317 | -from configglue.pyschema import ConfigOption, NO_DEFAULT |
318 | - |
319 | - |
320 | -class BoolConfigOption(ConfigOption): |
321 | - """A ConfigOption that is parsed into a bool""" |
322 | - |
323 | - def _get_default(self): |
324 | - return False |
325 | - |
326 | - def parse(self, value, raw=False): |
327 | - if raw: |
328 | - return value |
329 | - |
330 | - if value.lower() in ['y', '1', 'yes', 'on', 'true']: |
331 | - return True |
332 | - elif value.lower() in ['n', '0', 'no', 'off', 'false']: |
333 | - return False |
334 | - else: |
335 | - raise ValueError("Unable to determine boolosity of %r" % value) |
336 | - |
337 | - |
338 | -class IntConfigOption(ConfigOption): |
339 | - """A ConfigOption that is parsed into an int""" |
340 | - |
341 | - def _get_default(self): |
342 | - return 0 |
343 | - |
344 | - def parse(self, value, raw=False): |
345 | - if raw: |
346 | - return value |
347 | - |
348 | - return int(value) |
349 | - |
350 | - |
351 | -class LinesConfigOption(ConfigOption): |
352 | - """A ConfigOption that is parsed into a list of objects |
353 | - |
354 | - All items in the list need to be of the same type. The 'item' constructor |
355 | - argument determines the type of the list items. item should be another |
356 | - child of ConfigOption. |
357 | - |
358 | - self.require_parser will be True if the item provided in turn has |
359 | - require_parser == True. |
360 | - |
361 | - if remove_duplicates == True, duplicate elements in the lines will be |
362 | - removed. Only the first occurrence of any item will be kept, |
363 | - otherwise the general order of the list will be preserved. |
364 | - """ |
365 | - |
366 | - def _get_default(self): |
367 | - return [] |
368 | - |
369 | - def parse(self, value, parser=None, raw=False): |
370 | - def _parse_item(value): |
371 | - if self.require_parser: |
372 | - value = self.item.parse(value, parser=parser, raw=raw) |
373 | - else: |
374 | - value = self.item.parse(value, raw=raw) |
375 | - return value |
376 | - items = [_parse_item(x) for x in value.split('\n') if len(x)] |
377 | - if self.remove_duplicates: |
378 | - filtered_items = [] |
379 | - for item in items: |
380 | - if not item in filtered_items: |
381 | - filtered_items.append(item) |
382 | - items = filtered_items |
383 | - return items |
384 | - |
385 | - def __init__(self, item, raw=False, default=NO_DEFAULT, fatal=False, |
386 | - help='', action='store', remove_duplicates=False): |
387 | - super(LinesConfigOption, self).__init__(raw=raw, default=default, |
388 | - fatal=fatal, help=help, action=action) |
389 | - self.item = item |
390 | - self.require_parser = item.require_parser |
391 | - self.raw = item.raw |
392 | - self.remove_duplicates = remove_duplicates |
393 | - |
394 | -class StringConfigOption(ConfigOption): |
395 | - """A ConfigOption that is parsed into a string. |
396 | - |
397 | - If null==True, a value of 'None' will be parsed in to None instead of |
398 | - just leaving it as the string 'None'. |
399 | - """ |
400 | - |
401 | - def _get_default(self): |
402 | - return '' if not self.null else None |
403 | - |
404 | - def parse(self, value, raw=False): |
405 | - if raw: |
406 | - return value |
407 | - |
408 | - return unicode(value) |
409 | - |
410 | - def __init__(self, raw=False, default=NO_DEFAULT, fatal=False, null=False, |
411 | - help='', action='store'): |
412 | - self.null = null |
413 | - super(StringConfigOption, self).__init__(raw=raw, default=default, |
414 | - fatal=fatal, help=help, action=action) |
415 | - |
416 | - |
417 | -class TupleConfigOption(ConfigOption): |
418 | - """A ConfigOption that is parsed into a fixed-size tuple of strings. |
419 | - |
420 | - The number of items in the tuple should be specified with the 'length' |
421 | - constructor argument. |
422 | - """ |
423 | - |
424 | - def __init__(self, length=0, raw=False, default=NO_DEFAULT, fatal=False, |
425 | - help='', action='store'): |
426 | - super(TupleConfigOption, self).__init__(raw=raw, default=default, |
427 | - fatal=fatal, help=help, action=action) |
428 | - self.length = length |
429 | - |
430 | - def _get_default(self): |
431 | - return () |
432 | - |
433 | - def parse(self, value, raw=False): |
434 | - parts = [part.strip() for part in value.split(',')] |
435 | - if parts == ['()']: |
436 | - result = () |
437 | - elif self.length: |
438 | - # length is not 0, so length validation |
439 | - if len(parts) == self.length: |
440 | - result = tuple(parts) |
441 | - else: |
442 | - raise ValueError("Tuples need to be %d items long" % self.length) |
443 | - else: |
444 | - result = tuple(parts) |
445 | - # length is 0, so no length validation |
446 | - return result |
447 | - |
448 | - |
449 | -class DictConfigOption(ConfigOption): |
450 | - """A ConfigOption that is parsed into a dictionary. |
451 | - |
452 | - In the configuration file you'll need to specify the name of a section, |
453 | - and all that section's items will be parsed as a dictionary. |
454 | - |
455 | - The available keys for the dict are specified with the 'spec' constructor |
456 | - argument, that should be in turn a dictionary. spec's keys are the |
457 | - available keys for the config file, and spec's values should be |
458 | - ConfigOptions that will be used to parse the values in the config file. |
459 | - """ |
460 | - require_parser = True |
461 | - |
462 | - def __init__(self, spec=None, strict=False, raw=False, |
463 | - default=NO_DEFAULT, fatal=False, help='', action='store', |
464 | - item=None): |
465 | - if spec is None: |
466 | - spec = {} |
467 | - if item is None: |
468 | - item = StringConfigOption() |
469 | - self.spec = spec |
470 | - self.strict = strict |
471 | - self.item = item |
472 | - super(DictConfigOption, self).__init__(raw=raw, default=default, |
473 | - fatal=fatal, help=help, action=action) |
474 | - |
475 | - def _get_default(self): |
476 | - default = {} |
477 | - for key, value in self.spec.items(): |
478 | - default[key] = value.default |
479 | - return default |
480 | - |
481 | - def parse(self, section, parser=None, raw=False): |
482 | - parsed = dict(parser.items(section)) |
483 | - result = {} |
484 | - |
485 | - # parse config items according to spec |
486 | - for key, value in parsed.items(): |
487 | - if self.strict and not key in self.spec: |
488 | - raise ValueError("Invalid key %s in section %s" % (key, |
489 | - section)) |
490 | - option = self.spec.get(key, None) |
491 | - if option is None: |
492 | - # option not part of spec, but we are in non-strict mode |
493 | - # parse it using the default item parser |
494 | - option = self.item |
495 | - |
496 | - # parse option |
497 | - kwargs = {} |
498 | - if option.require_parser: |
499 | - kwargs['parser'] = parser |
500 | - if not raw: |
501 | - value = option.parse(value, **kwargs) |
502 | - result[key] = value |
503 | - |
504 | - # fill in missing items with default values |
505 | - for key in self.spec: |
506 | - if not key in parsed: |
507 | - option = self.spec[key] |
508 | - if option.fatal: |
509 | - raise ValueError("No option '%s' in section '%s'" % |
510 | - (key, section)) |
511 | - else: |
512 | - if not raw: |
513 | - value = option.default |
514 | - else: |
515 | - value = unicode(option.default) |
516 | - result[key] = value |
517 | - return result |
518 | - |
519 | - def get_extra_sections(self, section, parser): |
520 | - sections = [] |
521 | - for option in parser.options(section): |
522 | - option_obj = self.spec.get(option, self.item) |
523 | - is_dict_item = isinstance(option_obj, DictConfigOption) |
524 | - is_dict_lines_item = (hasattr(option_obj, 'item') and |
525 | - isinstance(option_obj.item, DictConfigOption)) |
526 | - |
527 | - if is_dict_item: |
528 | - base = option_obj |
529 | - elif is_dict_lines_item: |
530 | - base = option_obj.item |
531 | - else: |
532 | - continue |
533 | - |
534 | - value = parser.get(section, option, parse=False) |
535 | - names = value.split() |
536 | - sections.extend(names) |
537 | - |
538 | - # recurse |
539 | - for name in names: |
540 | - extra = base.get_extra_sections(name, parser) |
541 | - sections.extend(extra) |
542 | - |
543 | - return sections |
544 | - |
545 | |
546 | === modified file 'configglue/pyschema/parser.py' |
547 | --- configglue/pyschema/parser.py 2010-07-31 01:15:59 +0000 |
548 | +++ configglue/pyschema/parser.py 2010-12-19 19:01:37 +0000 |
549 | @@ -21,12 +21,21 @@ |
550 | import os |
551 | import string |
552 | |
553 | -from ConfigParser import (NoSectionError, DEFAULTSECT, |
554 | - InterpolationMissingOptionError, NoOptionError, |
555 | - ConfigParser as BaseConfigParser) |
556 | - |
557 | -from configglue.pyschema.options import DictConfigOption |
558 | - |
559 | +from ConfigParser import ( |
560 | + DEFAULTSECT, |
561 | + ConfigParser as BaseConfigParser, |
562 | + InterpolationMissingOptionError, |
563 | + NoOptionError, |
564 | + NoSectionError, |
565 | +) |
566 | + |
567 | +from configglue.pyschema.schema import DictConfigOption |
568 | + |
569 | + |
570 | +__all__ = [ |
571 | + 'SchemaValidationError', |
572 | + 'SchemaConfigParser', |
573 | +] |
574 | |
575 | CONFIG_FILE_ENCODING = 'utf-8' |
576 | |
577 | |
578 | === modified file 'configglue/pyschema/schema.py' |
579 | --- configglue/pyschema/schema.py 2010-08-06 19:29:19 +0000 |
580 | +++ configglue/pyschema/schema.py 2010-12-19 19:01:37 +0000 |
581 | @@ -17,12 +17,36 @@ |
582 | |
583 | from copy import deepcopy |
584 | |
585 | -from configglue.pyschema import ConfigOption, ConfigSection, super_vars |
586 | -from configglue.pyschema.options import LinesConfigOption, StringConfigOption |
587 | - |
588 | + |
589 | +__all__ = [ |
590 | + 'super_vars', |
591 | + 'BoolConfigOption', |
592 | + 'ConfigOption', |
593 | + 'ConfigSection', |
594 | + 'DictConfigOption', |
595 | + 'IntConfigOption', |
596 | + 'LinesConfigOption', |
597 | + 'Schema', |
598 | + 'StringConfigOption', |
599 | + 'TupleConfigOption', |
600 | +] |
601 | + |
602 | +NO_DEFAULT = object() |
603 | |
604 | _internal = object.__dict__.keys() + ['__module__'] |
605 | |
606 | + |
607 | +def super_vars(obj): |
608 | + """An extended version of vars() that walks all base classes.""" |
609 | + items = {} |
610 | + if hasattr(obj, '__mro__'): |
611 | + bases = map(vars, obj.__mro__) |
612 | + map(items.update, bases) |
613 | + else: |
614 | + items = vars(obj) |
615 | + return items |
616 | + |
617 | + |
618 | class Schema(object): |
619 | """A complete description of a system configuration. |
620 | |
621 | @@ -123,3 +147,347 @@ |
622 | return options |
623 | |
624 | |
625 | +class ConfigSection(object): |
626 | + """A group of options. |
627 | + |
628 | + This class is just a bag you can dump ConfigOptions in. |
629 | + |
630 | + After instantiating the Schema, each ConfigSection will know its own |
631 | + name. |
632 | + """ |
633 | + def __init__(self, name=''): |
634 | + self.name = name |
635 | + |
636 | + def __eq__(self, other): |
637 | + return (self.name == other.name and |
638 | + self.options() == other.options()) |
639 | + |
640 | + def __repr__(self): |
641 | + if self.name: |
642 | + name = " %s" % self.name |
643 | + else: |
644 | + name = '' |
645 | + value = "<ConfigSection%s>" % name |
646 | + return value |
647 | + |
648 | + def has_option(self, name): |
649 | + """Return True if a ConfigOption with the given name is available""" |
650 | + opt = getattr(self, name, None) |
651 | + return isinstance(opt, ConfigOption) |
652 | + |
653 | + def option(self, name): |
654 | + """Return a ConfigOption by name""" |
655 | + opt = getattr(self, name, None) |
656 | + assert opt is not None, "Invalid ConfigOption name '%s'" % name |
657 | + return opt |
658 | + |
659 | + def options(self): |
660 | + """Return a list of all available ConfigOptions within this section""" |
661 | + return [getattr(self, att) for att in vars(self) |
662 | + if isinstance(getattr(self, att), ConfigOption)] |
663 | + |
664 | + |
665 | +class ConfigOption(object): |
666 | + """Base class for Config Options. |
667 | + |
668 | + ConfigOptions are never bound to a particular conguration file, and |
669 | + simply describe one particular available option. |
670 | + |
671 | + They also know how to parse() the content of a config file in to the right |
672 | + type of object. |
673 | + |
674 | + If self.raw == True, then variable interpolation will not be carried out |
675 | + for this config option. |
676 | + |
677 | + If self.require_parser == True, then the parse() method will have a second |
678 | + argument, parser, that should receive the whole SchemaConfigParser to |
679 | + do the parsing. This is needed for config options that need to look at |
680 | + other parts of the config file to be able to carry out their parsing, |
681 | + like DictConfigOptions. |
682 | + |
683 | + If self.fatal == True, SchemaConfigParser's parse_all will raise an |
684 | + exception if no value for this option is provided in the configuration |
685 | + file. Otherwise, the self.default value will be used if the option is |
686 | + omitted. |
687 | + |
688 | + In runtime, after instantiating the Schema, each ConfigOption will also |
689 | + know its own name and to which section it belongs. |
690 | + """ |
691 | + |
692 | + require_parser = False |
693 | + |
694 | + def __init__(self, name='', raw=False, default=NO_DEFAULT, fatal=False, help='', |
695 | + section=None, action='store'): |
696 | + self.name = name |
697 | + self.raw = raw |
698 | + self.fatal = fatal |
699 | + if default is NO_DEFAULT: |
700 | + default = self._get_default() |
701 | + self.default = default |
702 | + self.help = help |
703 | + self.section = section |
704 | + self.action = action |
705 | + |
706 | + def __eq__(self, other): |
707 | + try: |
708 | + equal = (self.name == other.name and |
709 | + self.raw == other.raw and |
710 | + self.fatal == other.fatal and |
711 | + self.default == other.default and |
712 | + self.help == other.help) |
713 | + if self.section is not None and other.section is not None: |
714 | + # only test for section name to avoid recursion |
715 | + equal &= self.section.name == other.section.name |
716 | + else: |
717 | + equal &= (self.section is None and other.section is None) |
718 | + except AttributeError: |
719 | + equal = False |
720 | + |
721 | + return equal |
722 | + |
723 | + def __repr__(self): |
724 | + extra = ' raw' if self.raw else '' |
725 | + extra += ' fatal' if self.fatal else '' |
726 | + section = self.section.name if self.section is not None else None |
727 | + if section is not None: |
728 | + name = " %s.%s" % (section, self.name) |
729 | + elif self.name: |
730 | + name = " %s" % self.name |
731 | + else: |
732 | + name = '' |
733 | + value = "<ConfigOption%s%s>" % (name, extra) |
734 | + return value |
735 | + |
736 | + def _get_default(self): |
737 | + return None |
738 | + |
739 | + def parse(self, value): |
740 | + raise NotImplementedError() |
741 | + |
742 | + |
743 | +class BoolConfigOption(ConfigOption): |
744 | + """A ConfigOption that is parsed into a bool""" |
745 | + |
746 | + def _get_default(self): |
747 | + return False |
748 | + |
749 | + def parse(self, value, raw=False): |
750 | + if raw: |
751 | + return value |
752 | + |
753 | + if value.lower() in ['y', '1', 'yes', 'on', 'true']: |
754 | + return True |
755 | + elif value.lower() in ['n', '0', 'no', 'off', 'false']: |
756 | + return False |
757 | + else: |
758 | + raise ValueError("Unable to determine boolosity of %r" % value) |
759 | + |
760 | + |
761 | +class IntConfigOption(ConfigOption): |
762 | + """A ConfigOption that is parsed into an int""" |
763 | + |
764 | + def _get_default(self): |
765 | + return 0 |
766 | + |
767 | + def parse(self, value, raw=False): |
768 | + if raw: |
769 | + return value |
770 | + |
771 | + return int(value) |
772 | + |
773 | + |
774 | +class LinesConfigOption(ConfigOption): |
775 | + """A ConfigOption that is parsed into a list of objects |
776 | + |
777 | + All items in the list need to be of the same type. The 'item' constructor |
778 | + argument determines the type of the list items. item should be another |
779 | + child of ConfigOption. |
780 | + |
781 | + self.require_parser will be True if the item provided in turn has |
782 | + require_parser == True. |
783 | + |
784 | + if remove_duplicates == True, duplicate elements in the lines will be |
785 | + removed. Only the first occurrence of any item will be kept, |
786 | + otherwise the general order of the list will be preserved. |
787 | + """ |
788 | + |
789 | + def _get_default(self): |
790 | + return [] |
791 | + |
792 | + def parse(self, value, parser=None, raw=False): |
793 | + def _parse_item(value): |
794 | + if self.require_parser: |
795 | + value = self.item.parse(value, parser=parser, raw=raw) |
796 | + else: |
797 | + value = self.item.parse(value, raw=raw) |
798 | + return value |
799 | + items = [_parse_item(x) for x in value.split('\n') if len(x)] |
800 | + if self.remove_duplicates: |
801 | + filtered_items = [] |
802 | + for item in items: |
803 | + if not item in filtered_items: |
804 | + filtered_items.append(item) |
805 | + items = filtered_items |
806 | + return items |
807 | + |
808 | + def __init__(self, item, raw=False, default=NO_DEFAULT, fatal=False, |
809 | + help='', action='store', remove_duplicates=False): |
810 | + super(LinesConfigOption, self).__init__(raw=raw, default=default, |
811 | + fatal=fatal, help=help, action=action) |
812 | + self.item = item |
813 | + self.require_parser = item.require_parser |
814 | + self.raw = item.raw |
815 | + self.remove_duplicates = remove_duplicates |
816 | + |
817 | + |
818 | +class StringConfigOption(ConfigOption): |
819 | + """A ConfigOption that is parsed into a string. |
820 | + |
821 | + If null==True, a value of 'None' will be parsed in to None instead of |
822 | + just leaving it as the string 'None'. |
823 | + """ |
824 | + |
825 | + def _get_default(self): |
826 | + return '' if not self.null else None |
827 | + |
828 | + def parse(self, value, raw=False): |
829 | + if raw: |
830 | + return value |
831 | + |
832 | + return unicode(value) |
833 | + |
834 | + def __init__(self, raw=False, default=NO_DEFAULT, fatal=False, null=False, |
835 | + help='', action='store'): |
836 | + self.null = null |
837 | + super(StringConfigOption, self).__init__(raw=raw, default=default, |
838 | + fatal=fatal, help=help, action=action) |
839 | + |
840 | + |
841 | +class TupleConfigOption(ConfigOption): |
842 | + """A ConfigOption that is parsed into a fixed-size tuple of strings. |
843 | + |
844 | + The number of items in the tuple should be specified with the 'length' |
845 | + constructor argument. |
846 | + """ |
847 | + |
848 | + def __init__(self, length=0, raw=False, default=NO_DEFAULT, fatal=False, |
849 | + help='', action='store'): |
850 | + super(TupleConfigOption, self).__init__(raw=raw, default=default, |
851 | + fatal=fatal, help=help, action=action) |
852 | + self.length = length |
853 | + |
854 | + def _get_default(self): |
855 | + return () |
856 | + |
857 | + def parse(self, value, raw=False): |
858 | + parts = [part.strip() for part in value.split(',')] |
859 | + if parts == ['()']: |
860 | + result = () |
861 | + elif self.length: |
862 | + # length is not 0, so length validation |
863 | + if len(parts) == self.length: |
864 | + result = tuple(parts) |
865 | + else: |
866 | + raise ValueError("Tuples need to be %d items long" % self.length) |
867 | + else: |
868 | + result = tuple(parts) |
869 | + # length is 0, so no length validation |
870 | + return result |
871 | + |
872 | + |
873 | +class DictConfigOption(ConfigOption): |
874 | + """A ConfigOption that is parsed into a dictionary. |
875 | + |
876 | + In the configuration file you'll need to specify the name of a section, |
877 | + and all that section's items will be parsed as a dictionary. |
878 | + |
879 | + The available keys for the dict are specified with the 'spec' constructor |
880 | + argument, that should be in turn a dictionary. spec's keys are the |
881 | + available keys for the config file, and spec's values should be |
882 | + ConfigOptions that will be used to parse the values in the config file. |
883 | + """ |
884 | + require_parser = True |
885 | + |
886 | + def __init__(self, spec=None, strict=False, raw=False, |
887 | + default=NO_DEFAULT, fatal=False, help='', action='store', |
888 | + item=None): |
889 | + if spec is None: |
890 | + spec = {} |
891 | + if item is None: |
892 | + item = StringConfigOption() |
893 | + self.spec = spec |
894 | + self.strict = strict |
895 | + self.item = item |
896 | + super(DictConfigOption, self).__init__(raw=raw, default=default, |
897 | + fatal=fatal, help=help, action=action) |
898 | + |
899 | + def _get_default(self): |
900 | + default = {} |
901 | + for key, value in self.spec.items(): |
902 | + default[key] = value.default |
903 | + return default |
904 | + |
905 | + def parse(self, section, parser=None, raw=False): |
906 | + parsed = dict(parser.items(section)) |
907 | + result = {} |
908 | + |
909 | + # parse config items according to spec |
910 | + for key, value in parsed.items(): |
911 | + if self.strict and not key in self.spec: |
912 | + raise ValueError("Invalid key %s in section %s" % (key, |
913 | + section)) |
914 | + option = self.spec.get(key, None) |
915 | + if option is None: |
916 | + # option not part of spec, but we are in non-strict mode |
917 | + # parse it using the default item parser |
918 | + option = self.item |
919 | + |
920 | + # parse option |
921 | + kwargs = {} |
922 | + if option.require_parser: |
923 | + kwargs['parser'] = parser |
924 | + if not raw: |
925 | + value = option.parse(value, **kwargs) |
926 | + result[key] = value |
927 | + |
928 | + # fill in missing items with default values |
929 | + for key in self.spec: |
930 | + if not key in parsed: |
931 | + option = self.spec[key] |
932 | + if option.fatal: |
933 | + raise ValueError("No option '%s' in section '%s'" % |
934 | + (key, section)) |
935 | + else: |
936 | + if not raw: |
937 | + value = option.default |
938 | + else: |
939 | + value = unicode(option.default) |
940 | + result[key] = value |
941 | + return result |
942 | + |
943 | + def get_extra_sections(self, section, parser): |
944 | + sections = [] |
945 | + for option in parser.options(section): |
946 | + option_obj = self.spec.get(option, self.item) |
947 | + is_dict_item = isinstance(option_obj, DictConfigOption) |
948 | + is_dict_lines_item = (hasattr(option_obj, 'item') and |
949 | + isinstance(option_obj.item, DictConfigOption)) |
950 | + |
951 | + if is_dict_item: |
952 | + base = option_obj |
953 | + elif is_dict_lines_item: |
954 | + base = option_obj.item |
955 | + else: |
956 | + continue |
957 | + |
958 | + value = parser.get(section, option, parse=False) |
959 | + names = value.split() |
960 | + sections.extend(names) |
961 | + |
962 | + # recurse |
963 | + for name in names: |
964 | + extra = base.get_extra_sections(name, parser) |
965 | + sections.extend(extra) |
966 | + |
967 | + return sections |
968 | + |
969 | |
970 | === added file 'configglue/utils.py' |
971 | --- configglue/utils.py 1970-01-01 00:00:00 +0000 |
972 | +++ configglue/utils.py 2010-12-19 19:01:37 +0000 |
973 | @@ -0,0 +1,162 @@ |
974 | +############################################################################### |
975 | +# |
976 | +# configglue -- glue for your apps' configuration |
977 | +# |
978 | +# A library for simple, DRY configuration of applications |
979 | +# |
980 | +# (C) 2009--2010 by Canonical Ltd. |
981 | +# originally by John R. Lenton <john.lenton@canonical.com> |
982 | +# incorporating schemaconfig as configglue.pyschema |
983 | +# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@canonical.com> |
984 | +# |
985 | +# Released under the BSD License (see the file LICENSE) |
986 | +# |
987 | +# For bug reports, support, and new releases: http://launchpad.net/configglue |
988 | +# |
989 | +############################################################################### |
990 | + |
991 | +import __builtin__ |
992 | +import sys |
993 | +from optparse import OptionParser |
994 | +from collections import namedtuple |
995 | + |
996 | +from configglue.inischema import ( |
997 | + parsers, |
998 | + AttributedConfigParser, |
999 | +) |
1000 | +from configglue.pyschema.parser import SchemaConfigParser |
1001 | +from configglue.pyschema.schema import ( |
1002 | + BoolConfigOption, |
1003 | + ConfigSection, |
1004 | + IntConfigOption, |
1005 | + LinesConfigOption, |
1006 | + Schema, |
1007 | + StringConfigOption, |
1008 | +) |
1009 | + |
1010 | + |
1011 | +__all__ = [ |
1012 | + 'configglue', |
1013 | + 'ini2schema', |
1014 | + 'schemaconfigglue', |
1015 | +] |
1016 | + |
1017 | +SchemaGlue = namedtuple("SchemaGlue", "schema_parser option_parser options args") |
1018 | +IniGlue = namedtuple("IniGlue", " option_parser options args") |
1019 | + |
1020 | + |
1021 | +def ini2schema(fd, p=None): |
1022 | + """ |
1023 | + Turn a fd that refers to a INI-style schema definition into a |
1024 | + SchemaConfigParser object |
1025 | + |
1026 | + @param fd: file-like object to read the schema from |
1027 | + @param p: a parser to use. If not set, uses AttributedConfigParser |
1028 | + """ |
1029 | + if p is None: |
1030 | + p = AttributedConfigParser() |
1031 | + p.readfp(fd) |
1032 | + p.parse_all() |
1033 | + |
1034 | + parser2option = {'unicode': StringConfigOption, |
1035 | + 'int': IntConfigOption, |
1036 | + 'bool': BoolConfigOption, |
1037 | + 'lines': LinesConfigOption} |
1038 | + |
1039 | + class MySchema(Schema): |
1040 | + pass |
1041 | + |
1042 | + for section_name in p.sections(): |
1043 | + if section_name == '__main__': |
1044 | + section = MySchema |
1045 | + else: |
1046 | + section = ConfigSection() |
1047 | + setattr(MySchema, section_name, section) |
1048 | + for option_name in p.options(section_name): |
1049 | + option = p.get(section_name, option_name) |
1050 | + |
1051 | + parser = option.attrs.pop('parser', 'unicode') |
1052 | + parser_args = option.attrs.pop('parser.args', '').split() |
1053 | + parser_fun = getattr(parsers, parser, None) |
1054 | + if parser_fun is None: |
1055 | + parser_fun = getattr(__builtin__, parser, None) |
1056 | + if parser_fun is None: |
1057 | + parser_fun = lambda x: x |
1058 | + |
1059 | + attrs = {} |
1060 | + option_help = option.attrs.pop('help', None) |
1061 | + if option_help is not None: |
1062 | + attrs['help'] = option_help |
1063 | + if not option.is_empty: |
1064 | + attrs['default'] = parser_fun(option.value, *parser_args) |
1065 | + option_action = option.attrs.pop('action', None) |
1066 | + if option_action is not None: |
1067 | + attrs['action'] = option_action |
1068 | + |
1069 | + klass = parser2option.get(parser, StringConfigOption) |
1070 | + if parser == 'lines': |
1071 | + instance = klass(StringConfigOption(), **attrs) |
1072 | + else: |
1073 | + instance = klass(**attrs) |
1074 | + setattr(section, option_name, instance) |
1075 | + |
1076 | + return SchemaConfigParser(MySchema()) |
1077 | + |
1078 | + |
1079 | +def schemaconfigglue(parser, op=None, argv=None): |
1080 | + """Populate an OptionParser with options and defaults taken from a |
1081 | + fully loaded SchemaConfigParser. |
1082 | + """ |
1083 | + |
1084 | + def long_name(option): |
1085 | + if option.section.name == '__main__': |
1086 | + return option.name |
1087 | + return option.section.name + '_' + option.name |
1088 | + |
1089 | + def opt_name(option): |
1090 | + return long_name(option).replace('-', '_') |
1091 | + |
1092 | + if op is None: |
1093 | + op = OptionParser() |
1094 | + if argv is None: |
1095 | + argv = sys.argv[1:] |
1096 | + schema = parser.schema |
1097 | + |
1098 | + for section in schema.sections(): |
1099 | + if section.name == '__main__': |
1100 | + og = op |
1101 | + else: |
1102 | + og = op.add_option_group(section.name) |
1103 | + for option in section.options(): |
1104 | + kwargs = {} |
1105 | + if option.help: |
1106 | + kwargs['help'] = option.help |
1107 | + kwargs['default'] = parser.get(section.name, option.name) |
1108 | + kwargs['action'] = option.action |
1109 | + og.add_option('--' + long_name(option), **kwargs) |
1110 | + options, args = op.parse_args(argv) |
1111 | + |
1112 | + for section in schema.sections(): |
1113 | + for option in section.options(): |
1114 | + value = getattr(options, opt_name(option)) |
1115 | + if parser.get(section.name, option.name) != value: |
1116 | + # the value has been overridden by an argument; |
1117 | + # update it. |
1118 | + parser.set(section.name, option.name, value) |
1119 | + |
1120 | + return IniGlue(op, options, args) |
1121 | + |
1122 | + |
1123 | +def configglue(schema_class, configs, usage=None): |
1124 | + scp = SchemaConfigParser(schema_class()) |
1125 | + scp.read(configs) |
1126 | + if usage is not None: |
1127 | + op = OptionParser(usage=usage) |
1128 | + else: |
1129 | + op = None |
1130 | + parser, opts, args = schemaconfigglue(scp, op=op) |
1131 | + is_valid, reasons = scp.is_valid(report=True) |
1132 | + if not is_valid: |
1133 | + parser.error(reasons[0]) |
1134 | + return SchemaGlue(scp, parser, opts, args) |
1135 | + |
1136 | |
1137 | === modified file 'tests/pyschema/test_glue2glue.py' |
1138 | --- tests/pyschema/test_glue2glue.py 2010-08-04 12:42:29 +0000 |
1139 | +++ tests/pyschema/test_glue2glue.py 2010-12-19 19:01:37 +0000 |
1140 | @@ -21,7 +21,10 @@ |
1141 | from StringIO import StringIO |
1142 | |
1143 | from configglue.inischema import configglue |
1144 | -from configglue.pyschema import schemaconfigglue, ini2schema |
1145 | +from configglue.utils import ( |
1146 | + ini2schema, |
1147 | + schemaconfigglue, |
1148 | +) |
1149 | |
1150 | |
1151 | class TestGlueConvertor(unittest.TestCase): |
1152 | |
1153 | === removed file 'tests/pyschema/test_options.py' |
1154 | --- tests/pyschema/test_options.py 2010-08-04 12:42:29 +0000 |
1155 | +++ tests/pyschema/test_options.py 1970-01-01 00:00:00 +0000 |
1156 | @@ -1,511 +0,0 @@ |
1157 | -############################################################################### |
1158 | -# |
1159 | -# configglue -- glue for your apps' configuration |
1160 | -# |
1161 | -# A library for simple, DRY configuration of applications |
1162 | -# |
1163 | -# (C) 2009--2010 by Canonical Ltd. |
1164 | -# originally by John R. Lenton <john.lenton@canonical.com> |
1165 | -# incorporating schemaconfig as configglue.pyschema |
1166 | -# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@canonical.com> |
1167 | -# |
1168 | -# Released under the BSD License (see the file LICENSE) |
1169 | -# |
1170 | -# For bug reports, support, and new releases: http://launchpad.net/configglue |
1171 | -# |
1172 | -############################################################################### |
1173 | - |
1174 | -import unittest |
1175 | -from StringIO import StringIO |
1176 | - |
1177 | -from configglue.pyschema.options import (BoolConfigOption, IntConfigOption, |
1178 | - StringConfigOption, LinesConfigOption, TupleConfigOption, DictConfigOption) |
1179 | -from configglue.pyschema.parser import SchemaConfigParser |
1180 | -from configglue.pyschema.schema import Schema |
1181 | - |
1182 | - |
1183 | -class TestStringConfigOption(unittest.TestCase): |
1184 | - def test_init_no_args(self): |
1185 | - opt = StringConfigOption() |
1186 | - self.assertFalse(opt.null) |
1187 | - |
1188 | - def test_init_null(self): |
1189 | - opt = StringConfigOption(null=True) |
1190 | - self.assertTrue(opt.null) |
1191 | - |
1192 | - def test_parse_string(self): |
1193 | - class MySchema(Schema): |
1194 | - foo = StringConfigOption(null=True) |
1195 | - config = StringIO("[__main__]\nfoo = 42") |
1196 | - expected_values = {'__main__': {'foo': '42'}} |
1197 | - schema = MySchema() |
1198 | - parser = SchemaConfigParser(schema) |
1199 | - parser.readfp(config) |
1200 | - self.assertEqual(parser.values(), expected_values) |
1201 | - |
1202 | - config = StringIO("[__main__]\nfoo = ") |
1203 | - expected_values = {'__main__': {'foo': ''}} |
1204 | - parser = SchemaConfigParser(schema) |
1205 | - parser.readfp(config) |
1206 | - self.assertEqual(parser.values(), expected_values) |
1207 | - |
1208 | - config = StringIO("[__main__]\nfoo = None") |
1209 | - expected_values = {'__main__': {'foo': None}} |
1210 | - parser = SchemaConfigParser(schema) |
1211 | - parser.readfp(config) |
1212 | - self.assertEqual(parser.values(), expected_values) |
1213 | - |
1214 | - class MySchema(Schema): |
1215 | - foo = StringConfigOption() |
1216 | - config = StringIO("[__main__]\nfoo = None") |
1217 | - expected_values = {'__main__': {'foo': 'None'}} |
1218 | - schema = MySchema() |
1219 | - parser = SchemaConfigParser(schema) |
1220 | - parser.readfp(config) |
1221 | - self.assertEqual(parser.values(), expected_values) |
1222 | - |
1223 | - def test_default(self): |
1224 | - opt = StringConfigOption() |
1225 | - self.assertEqual(opt.default, '') |
1226 | - |
1227 | - def test_default_null(self): |
1228 | - opt = StringConfigOption(null=True) |
1229 | - self.assertEqual(opt.default, None) |
1230 | - |
1231 | - |
1232 | -class TestIntConfigOption(unittest.TestCase): |
1233 | - def test_parse_int(self): |
1234 | - class MySchema(Schema): |
1235 | - foo = IntConfigOption() |
1236 | - config = StringIO("[__main__]\nfoo = 42") |
1237 | - expected_values = {'__main__': {'foo': 42}} |
1238 | - schema = MySchema() |
1239 | - parser = SchemaConfigParser(schema) |
1240 | - parser.readfp(config) |
1241 | - self.assertEqual(parser.values(), expected_values) |
1242 | - |
1243 | - config = StringIO("[__main__]\nfoo =") |
1244 | - parser = SchemaConfigParser(schema) |
1245 | - parser.readfp(config) |
1246 | - self.assertRaises(ValueError, parser.values) |
1247 | - |
1248 | - config = StringIO("[__main__]\nfoo = bla") |
1249 | - parser = SchemaConfigParser(schema) |
1250 | - parser.readfp(config) |
1251 | - self.assertRaises(ValueError, parser.values) |
1252 | - |
1253 | - def test_default(self): |
1254 | - opt = IntConfigOption() |
1255 | - self.assertEqual(opt.default, 0) |
1256 | - |
1257 | - |
1258 | -class TestBoolConfigOption(unittest.TestCase): |
1259 | - def test_parse_bool(self): |
1260 | - class MySchema(Schema): |
1261 | - foo = BoolConfigOption() |
1262 | - config = StringIO("[__main__]\nfoo = Yes") |
1263 | - expected_values = {'__main__': {'foo': True}} |
1264 | - schema = MySchema() |
1265 | - parser = SchemaConfigParser(schema) |
1266 | - parser.readfp(config) |
1267 | - self.assertEqual(parser.values(), expected_values) |
1268 | - |
1269 | - config = StringIO("[__main__]\nfoo = tRuE") |
1270 | - parser = SchemaConfigParser(schema) |
1271 | - parser.readfp(config) |
1272 | - self.assertEqual(parser.values(), expected_values) |
1273 | - |
1274 | - config = StringIO("[__main__]\nfoo =") |
1275 | - parser = SchemaConfigParser(schema) |
1276 | - parser.readfp(config) |
1277 | - self.assertRaises(ValueError, parser.values) |
1278 | - |
1279 | - config = StringIO("[__main__]\nfoo = bla") |
1280 | - parser = SchemaConfigParser(schema) |
1281 | - parser.readfp(config) |
1282 | - self.assertRaises(ValueError, parser.values) |
1283 | - |
1284 | - def test_default(self): |
1285 | - opt = BoolConfigOption() |
1286 | - self.assertEqual(opt.default, False) |
1287 | - |
1288 | - |
1289 | -class TestLinesConfigOption(unittest.TestCase): |
1290 | - def test_parse_int_lines(self): |
1291 | - class MySchema(Schema): |
1292 | - foo = LinesConfigOption(item=IntConfigOption()) |
1293 | - |
1294 | - config = StringIO("[__main__]\nfoo = 42\n 43\n 44") |
1295 | - expected_values = {'__main__': {'foo': [42, 43, 44]}} |
1296 | - schema = MySchema() |
1297 | - parser = SchemaConfigParser(schema) |
1298 | - parser.readfp(config) |
1299 | - self.assertEqual(parser.values(), expected_values) |
1300 | - |
1301 | - def test_parse_bool_lines(self): |
1302 | - class MySchema(Schema): |
1303 | - foo = LinesConfigOption(item=BoolConfigOption()) |
1304 | - schema = MySchema() |
1305 | - config = StringIO("[__main__]\nfoo = tRuE\n No\n 0\n 1") |
1306 | - expected_values = {'__main__': {'foo': [True, False, False, True]}} |
1307 | - parser = SchemaConfigParser(schema) |
1308 | - parser.readfp(config) |
1309 | - self.assertEqual(expected_values, parser.values()) |
1310 | - |
1311 | - def test_parse_bool_empty_lines(self): |
1312 | - class MySchema(Schema): |
1313 | - foo = LinesConfigOption(item=BoolConfigOption()) |
1314 | - schema = MySchema() |
1315 | - config = StringIO("[__main__]\nfoo =") |
1316 | - parser = SchemaConfigParser(schema) |
1317 | - parser.readfp(config) |
1318 | - expected_values = {'__main__': {'foo': []}} |
1319 | - self.assertEqual(expected_values, parser.values()) |
1320 | - |
1321 | - def test_parse_bool_invalid_lines(self): |
1322 | - class MySchema(Schema): |
1323 | - foo = LinesConfigOption(item=BoolConfigOption()) |
1324 | - schema = MySchema() |
1325 | - config = StringIO("[__main__]\nfoo = bla") |
1326 | - parser = SchemaConfigParser(schema) |
1327 | - parser.readfp(config) |
1328 | - self.assertRaises(ValueError, parser.values) |
1329 | - |
1330 | - config = StringIO("[__main__]\nfoo = True\n bla") |
1331 | - parser = SchemaConfigParser(schema) |
1332 | - parser.readfp(config) |
1333 | - self.assertRaises(ValueError, parser.values) |
1334 | - |
1335 | - def test_default(self): |
1336 | - opt = LinesConfigOption(item=IntConfigOption()) |
1337 | - self.assertEqual(opt.default, []) |
1338 | - |
1339 | - def test_remove_duplicates(self): |
1340 | - class MySchema(Schema): |
1341 | - foo = LinesConfigOption(item=StringConfigOption(), |
1342 | - remove_duplicates=True) |
1343 | - schema = MySchema() |
1344 | - config = StringIO("[__main__]\nfoo = bla\n blah\n bla") |
1345 | - parser = SchemaConfigParser(schema) |
1346 | - parser.readfp(config) |
1347 | - self.assertEquals({'__main__': {'foo': ['bla', 'blah']}}, |
1348 | - parser.values()) |
1349 | - |
1350 | - def test_remove_dict_duplicates(self): |
1351 | - class MyOtherSchema(Schema): |
1352 | - foo = LinesConfigOption(item=DictConfigOption(), |
1353 | - remove_duplicates=True) |
1354 | - schema = MyOtherSchema() |
1355 | - config = StringIO("[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz") |
1356 | - parser = SchemaConfigParser(schema) |
1357 | - parser.readfp(config) |
1358 | - self.assertEquals({'__main__': {'foo': [{'bar': 'baz'}]}}, |
1359 | - parser.values()) |
1360 | - |
1361 | -class TestTupleConfigOption(unittest.TestCase): |
1362 | - def test_init(self): |
1363 | - opt = TupleConfigOption(2) |
1364 | - self.assertEqual(opt.length, 2) |
1365 | - |
1366 | - def test_init_no_length(self): |
1367 | - opt = TupleConfigOption() |
1368 | - self.assertEqual(opt.length, 0) |
1369 | - self.assertEqual(opt.default, ()) |
1370 | - |
1371 | - def test_parse_no_length(self): |
1372 | - class MySchema(Schema): |
1373 | - foo = TupleConfigOption() |
1374 | - |
1375 | - config = StringIO('[__main__]\nfoo=1,2,3,4') |
1376 | - expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}} |
1377 | - parser = SchemaConfigParser(MySchema()) |
1378 | - parser.readfp(config) |
1379 | - self.assertEqual(parser.values(), expected_values) |
1380 | - |
1381 | - def test_parse_tuple(self): |
1382 | - class MySchema(Schema): |
1383 | - foo = TupleConfigOption(length=4) |
1384 | - config = StringIO('[__main__]\nfoo = 1, 2, 3, 4') |
1385 | - expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}} |
1386 | - schema = MySchema() |
1387 | - parser = SchemaConfigParser(schema) |
1388 | - parser.readfp(config) |
1389 | - self.assertEqual(parser.values(), expected_values) |
1390 | - |
1391 | - config = StringIO('[__main__]\nfoo = 1, 2, 3') |
1392 | - parser = SchemaConfigParser(schema) |
1393 | - parser.readfp(config) |
1394 | - self.assertRaises(ValueError, parser.values) |
1395 | - |
1396 | - config = StringIO('[__main__]\nfoo = ') |
1397 | - parser = SchemaConfigParser(schema) |
1398 | - parser.readfp(config) |
1399 | - self.assertRaises(ValueError, parser.values) |
1400 | - |
1401 | - def test_default(self): |
1402 | - opt = TupleConfigOption(2) |
1403 | - self.assertEqual(opt.default, ()) |
1404 | - |
1405 | - |
1406 | -class TestDictConfigOption(unittest.TestCase): |
1407 | - def test_init(self): |
1408 | - opt = DictConfigOption() |
1409 | - self.assertEqual(opt.spec, {}) |
1410 | - self.assertEqual(opt.strict, False) |
1411 | - |
1412 | - spec = {'a': IntConfigOption(), 'b': BoolConfigOption()} |
1413 | - opt = DictConfigOption(spec) |
1414 | - self.assertEqual(opt.spec, spec) |
1415 | - self.assertEqual(opt.strict, False) |
1416 | - |
1417 | - opt = DictConfigOption(spec, strict=True) |
1418 | - self.assertEqual(opt.spec, spec) |
1419 | - self.assertEqual(opt.strict, True) |
1420 | - |
1421 | - def test_get_extra_sections(self): |
1422 | - class MySchema(Schema): |
1423 | - foo = DictConfigOption(item=DictConfigOption()) |
1424 | - |
1425 | - config = StringIO(""" |
1426 | -[__main__] |
1427 | -foo=dict1 |
1428 | -[dict1] |
1429 | -bar=dict2 |
1430 | -[dict2] |
1431 | -baz=42 |
1432 | -""") |
1433 | - parser = SchemaConfigParser(MySchema()) |
1434 | - parser.readfp(config) |
1435 | - expected = ['dict2'] |
1436 | - |
1437 | - opt = DictConfigOption(item=DictConfigOption()) |
1438 | - extra = opt.get_extra_sections('dict1', parser) |
1439 | - self.assertEqual(extra, expected) |
1440 | - |
1441 | - def test_parse_dict(self): |
1442 | - class MySchema(Schema): |
1443 | - foo = DictConfigOption({'bar': StringConfigOption(), |
1444 | - 'baz': IntConfigOption(), |
1445 | - 'bla': BoolConfigOption(), |
1446 | - }) |
1447 | - config = StringIO("""[__main__] |
1448 | -foo = mydict |
1449 | -[mydict] |
1450 | -bar=baz |
1451 | -baz=42 |
1452 | -bla=Yes |
1453 | -""") |
1454 | - expected_values = { |
1455 | - '__main__': { |
1456 | - 'foo': {'bar': 'baz', 'baz': 42, 'bla': True} |
1457 | - } |
1458 | - } |
1459 | - |
1460 | - schema = MySchema() |
1461 | - parser = SchemaConfigParser(schema) |
1462 | - parser.readfp(config) |
1463 | - self.assertEqual(parser.values(), expected_values) |
1464 | - |
1465 | - def test_parse_raw(self): |
1466 | - class MySchema(Schema): |
1467 | - foo = DictConfigOption({'bar': StringConfigOption(), |
1468 | - 'baz': IntConfigOption(), |
1469 | - 'bla': BoolConfigOption(), |
1470 | - }) |
1471 | - config = StringIO("""[__main__] |
1472 | -foo = mydict |
1473 | -[mydict] |
1474 | -baz=42 |
1475 | -""") |
1476 | - expected = {'bar': '', 'baz': '42', 'bla': 'False'} |
1477 | - |
1478 | - schema = MySchema() |
1479 | - parser = SchemaConfigParser(schema) |
1480 | - parser.readfp(config) |
1481 | - parsed = schema.foo.parse('mydict', parser, True) |
1482 | - self.assertEqual(parsed, expected) |
1483 | - |
1484 | - def test_parse_invalid_key_in_parsed(self): |
1485 | - class MySchema(Schema): |
1486 | - foo = DictConfigOption({'bar': IntConfigOption()}) |
1487 | - |
1488 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbaz=2") |
1489 | - expected_values = {'__main__': {'foo': {'bar': 0, 'baz': '2'}}} |
1490 | - parser = SchemaConfigParser(MySchema()) |
1491 | - parser.readfp(config) |
1492 | - self.assertEqual(parser.values(), expected_values) |
1493 | - |
1494 | - def test_parse_invalid_key_in_spec(self): |
1495 | - class MySchema(Schema): |
1496 | - foo = DictConfigOption({'bar': IntConfigOption(), |
1497 | - 'baz': IntConfigOption(fatal=True)}) |
1498 | - |
1499 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
1500 | - parser = SchemaConfigParser(MySchema()) |
1501 | - parser.readfp(config) |
1502 | - self.assertRaises(ValueError, parser.parse_all) |
1503 | - |
1504 | - def test_default(self): |
1505 | - opt = DictConfigOption({}) |
1506 | - self.assertEqual(opt.default, {}) |
1507 | - |
1508 | - def test_parse_no_strict_missing_args(self): |
1509 | - class MySchema(Schema): |
1510 | - foo = DictConfigOption({'bar': IntConfigOption()}) |
1511 | - |
1512 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]") |
1513 | - expected_values = {'__main__': {'foo': {'bar': 0}}} |
1514 | - parser = SchemaConfigParser(MySchema()) |
1515 | - parser.readfp(config) |
1516 | - self.assertEqual(parser.values(), expected_values) |
1517 | - |
1518 | - def test_parse_no_strict_extra_args(self): |
1519 | - class MySchema(Schema): |
1520 | - foo = DictConfigOption() |
1521 | - |
1522 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
1523 | - expected_values = {'__main__': {'foo': {'bar': '2'}}} |
1524 | - parser = SchemaConfigParser(MySchema()) |
1525 | - parser.readfp(config) |
1526 | - self.assertEqual(parser.values(), expected_values) |
1527 | - |
1528 | - def test_parse_no_strict_with_item(self): |
1529 | - class MySchema(Schema): |
1530 | - foo = DictConfigOption( |
1531 | - item=DictConfigOption( |
1532 | - item=IntConfigOption())) |
1533 | - config = StringIO(""" |
1534 | -[__main__] |
1535 | -foo = mydict |
1536 | -[mydict] |
1537 | -bar = baz |
1538 | -[baz] |
1539 | -wham=42 |
1540 | -""") |
1541 | - expected_values = {'__main__': {'foo': {'bar': {'wham': 42}}}} |
1542 | - parser = SchemaConfigParser(MySchema()) |
1543 | - parser.readfp(config) |
1544 | - self.assertEqual(parser.values(), expected_values) |
1545 | - |
1546 | - def test_parse_strict(self): |
1547 | - class MySchema(Schema): |
1548 | - spec = {'bar': IntConfigOption()} |
1549 | - foo = DictConfigOption(spec, strict=True) |
1550 | - |
1551 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
1552 | - expected_values = {'__main__': {'foo': {'bar': 2}}} |
1553 | - parser = SchemaConfigParser(MySchema()) |
1554 | - parser.readfp(config) |
1555 | - self.assertEqual(parser.values(), expected_values) |
1556 | - |
1557 | - def test_parse_strict_missing_vars(self): |
1558 | - class MySchema(Schema): |
1559 | - spec = {'bar': IntConfigOption(), |
1560 | - 'baz': IntConfigOption()} |
1561 | - foo = DictConfigOption(spec, strict=True) |
1562 | - |
1563 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
1564 | - expected_values = {'__main__': {'foo': {'bar': 2, 'baz': 0}}} |
1565 | - parser = SchemaConfigParser(MySchema()) |
1566 | - parser.readfp(config) |
1567 | - self.assertEqual(parser.values(), expected_values) |
1568 | - |
1569 | - def test_parse_strict_extra_vars(self): |
1570 | - class MySchema(Schema): |
1571 | - spec = {'bar': IntConfigOption()} |
1572 | - foo = DictConfigOption(spec, strict=True) |
1573 | - |
1574 | - config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3") |
1575 | - parser = SchemaConfigParser(MySchema()) |
1576 | - parser.readfp(config) |
1577 | - self.assertRaises(ValueError, parser.parse_all) |
1578 | - |
1579 | - |
1580 | -class TestLinesOfDictConfigOption(unittest.TestCase): |
1581 | - def test_parse_lines_of_dict(self): |
1582 | - class MySchema(Schema): |
1583 | - foo = LinesConfigOption( |
1584 | - DictConfigOption({'bar': StringConfigOption(), |
1585 | - 'baz': IntConfigOption(), |
1586 | - 'bla': BoolConfigOption(), |
1587 | - })) |
1588 | - config = StringIO("""[__main__] |
1589 | -foo = mylist0 |
1590 | - mylist1 |
1591 | -[mylist0] |
1592 | -bar=baz |
1593 | -baz=42 |
1594 | -bla=Yes |
1595 | -[mylist1] |
1596 | -bar=zort |
1597 | -baz=123 |
1598 | -bla=0 |
1599 | -""") |
1600 | - expected_values = { |
1601 | - '__main__': {'foo': [{'bar': 'baz', 'baz': 42, 'bla': True}, |
1602 | - {'bar': 'zort', 'baz': 123, 'bla': False}, |
1603 | - ]}} |
1604 | - |
1605 | - schema = MySchema() |
1606 | - parser = SchemaConfigParser(schema) |
1607 | - parser.readfp(config) |
1608 | - self.assertEqual(parser.values(), expected_values) |
1609 | - |
1610 | - |
1611 | -class TestDictWithDicts(unittest.TestCase): |
1612 | - def test_parse_dict_with_dicts(self): |
1613 | - innerspec = {'bar': StringConfigOption(), |
1614 | - 'baz': IntConfigOption(), |
1615 | - 'bla': BoolConfigOption(), |
1616 | - } |
1617 | - spec = {'name': StringConfigOption(), |
1618 | - 'size': IntConfigOption(), |
1619 | - 'options': DictConfigOption(innerspec)} |
1620 | - class MySchema(Schema): |
1621 | - foo = DictConfigOption(spec) |
1622 | - config = StringIO("""[__main__] |
1623 | -foo = outerdict |
1624 | -[outerdict] |
1625 | -options = innerdict |
1626 | -[innerdict] |
1627 | -bar = something |
1628 | -baz = 42 |
1629 | -""") |
1630 | - expected_values = { |
1631 | - '__main__': {'foo': {'name': '', 'size': 0, |
1632 | - 'options': {'bar': 'something', 'baz': 42, |
1633 | - 'bla': False}}}} |
1634 | - schema = MySchema() |
1635 | - parser = SchemaConfigParser(schema) |
1636 | - parser.readfp(config) |
1637 | - self.assertEqual(parser.values(), expected_values) |
1638 | - |
1639 | - |
1640 | -class TestListOfTuples(unittest.TestCase): |
1641 | - def setUp(self): |
1642 | - class MySchema(Schema): |
1643 | - foo = LinesConfigOption(item=TupleConfigOption(length=3)) |
1644 | - schema = MySchema() |
1645 | - self.parser = SchemaConfigParser(schema) |
1646 | - |
1647 | - def test_parse_list_of_tuples(self): |
1648 | - config = StringIO('[__main__]\nfoo = a, b, c\n d, e, f') |
1649 | - expected_values = { |
1650 | - '__main__': {'foo': [('a', 'b', 'c'), ('d', 'e', 'f')]}} |
1651 | - self.parser.readfp(config) |
1652 | - self.assertEqual(self.parser.values(), expected_values) |
1653 | - |
1654 | - def test_parse_wrong_tuple_size(self): |
1655 | - config = StringIO('[__main__]\nfoo = a, b, c\n d, e') |
1656 | - self.parser.readfp(config) |
1657 | - self.assertRaises(ValueError, self.parser.values) |
1658 | - |
1659 | - def test_parse_empty_tuple(self): |
1660 | - config = StringIO('[__main__]\nfoo=()') |
1661 | - expected_values = {'__main__': {'foo': [()]}} |
1662 | - self.parser.readfp(config) |
1663 | - self.assertEqual(self.parser.values(), expected_values) |
1664 | - |
1665 | - |
1666 | -if __name__ == '__main__': |
1667 | - unittest.main() |
1668 | |
1669 | === modified file 'tests/pyschema/test_parser.py' |
1670 | --- tests/pyschema/test_parser.py 2010-08-04 12:42:29 +0000 |
1671 | +++ tests/pyschema/test_parser.py 2010-12-19 19:01:37 +0000 |
1672 | @@ -25,12 +25,22 @@ |
1673 | from ConfigParser import (InterpolationMissingOptionError, |
1674 | InterpolationDepthError, NoSectionError) |
1675 | |
1676 | -from configglue.pyschema import ConfigSection |
1677 | -from configglue.pyschema.options import (BoolConfigOption, DictConfigOption, |
1678 | - IntConfigOption, StringConfigOption, LinesConfigOption, TupleConfigOption) |
1679 | -from configglue.pyschema.schema import Schema |
1680 | -from configglue.pyschema.parser import (NoOptionError, SchemaConfigParser, |
1681 | - SchemaValidationError, CONFIG_FILE_ENCODING) |
1682 | +from configglue.pyschema.parser import ( |
1683 | + CONFIG_FILE_ENCODING, |
1684 | + NoOptionError, |
1685 | + SchemaConfigParser, |
1686 | + SchemaValidationError, |
1687 | +) |
1688 | +from configglue.pyschema.schema import ( |
1689 | + BoolConfigOption, |
1690 | + ConfigSection, |
1691 | + DictConfigOption, |
1692 | + IntConfigOption, |
1693 | + LinesConfigOption, |
1694 | + Schema, |
1695 | + StringConfigOption, |
1696 | + TupleConfigOption, |
1697 | +) |
1698 | |
1699 | |
1700 | class TestIncludes(unittest.TestCase): |
1701 | |
1702 | === modified file 'tests/pyschema/test_schema.py' |
1703 | --- tests/pyschema/test_schema.py 2010-08-06 19:47:09 +0000 |
1704 | +++ tests/pyschema/test_schema.py 2010-12-19 19:01:37 +0000 |
1705 | @@ -16,11 +16,19 @@ |
1706 | ############################################################################### |
1707 | |
1708 | import unittest |
1709 | +from StringIO import StringIO |
1710 | |
1711 | -from configglue.pyschema import ConfigSection |
1712 | -from configglue.pyschema.options import (BoolConfigOption, |
1713 | - IntConfigOption, LinesConfigOption) |
1714 | -from configglue.pyschema.schema import Schema |
1715 | +from configglue.pyschema.parser import SchemaConfigParser |
1716 | +from configglue.pyschema.schema import ( |
1717 | + BoolConfigOption, |
1718 | + ConfigSection, |
1719 | + DictConfigOption, |
1720 | + IntConfigOption, |
1721 | + LinesConfigOption, |
1722 | + Schema, |
1723 | + StringConfigOption, |
1724 | + TupleConfigOption, |
1725 | +) |
1726 | |
1727 | |
1728 | class TestSchema(unittest.TestCase): |
1729 | @@ -146,3 +154,485 @@ |
1730 | self.assertFalse(hasattr(self.other.foo, 'baz')) |
1731 | |
1732 | |
1733 | +class TestStringConfigOption(unittest.TestCase): |
1734 | + def test_init_no_args(self): |
1735 | + opt = StringConfigOption() |
1736 | + self.assertFalse(opt.null) |
1737 | + |
1738 | + def test_init_null(self): |
1739 | + opt = StringConfigOption(null=True) |
1740 | + self.assertTrue(opt.null) |
1741 | + |
1742 | + def test_parse_string(self): |
1743 | + class MySchema(Schema): |
1744 | + foo = StringConfigOption(null=True) |
1745 | + config = StringIO("[__main__]\nfoo = 42") |
1746 | + expected_values = {'__main__': {'foo': '42'}} |
1747 | + schema = MySchema() |
1748 | + parser = SchemaConfigParser(schema) |
1749 | + parser.readfp(config) |
1750 | + self.assertEqual(parser.values(), expected_values) |
1751 | + |
1752 | + config = StringIO("[__main__]\nfoo = ") |
1753 | + expected_values = {'__main__': {'foo': ''}} |
1754 | + parser = SchemaConfigParser(schema) |
1755 | + parser.readfp(config) |
1756 | + self.assertEqual(parser.values(), expected_values) |
1757 | + |
1758 | + config = StringIO("[__main__]\nfoo = None") |
1759 | + expected_values = {'__main__': {'foo': None}} |
1760 | + parser = SchemaConfigParser(schema) |
1761 | + parser.readfp(config) |
1762 | + self.assertEqual(parser.values(), expected_values) |
1763 | + |
1764 | + class MySchema(Schema): |
1765 | + foo = StringConfigOption() |
1766 | + config = StringIO("[__main__]\nfoo = None") |
1767 | + expected_values = {'__main__': {'foo': 'None'}} |
1768 | + schema = MySchema() |
1769 | + parser = SchemaConfigParser(schema) |
1770 | + parser.readfp(config) |
1771 | + self.assertEqual(parser.values(), expected_values) |
1772 | + |
1773 | + def test_default(self): |
1774 | + opt = StringConfigOption() |
1775 | + self.assertEqual(opt.default, '') |
1776 | + |
1777 | + def test_default_null(self): |
1778 | + opt = StringConfigOption(null=True) |
1779 | + self.assertEqual(opt.default, None) |
1780 | + |
1781 | + |
1782 | +class TestIntConfigOption(unittest.TestCase): |
1783 | + def test_parse_int(self): |
1784 | + class MySchema(Schema): |
1785 | + foo = IntConfigOption() |
1786 | + config = StringIO("[__main__]\nfoo = 42") |
1787 | + expected_values = {'__main__': {'foo': 42}} |
1788 | + schema = MySchema() |
1789 | + parser = SchemaConfigParser(schema) |
1790 | + parser.readfp(config) |
1791 | + self.assertEqual(parser.values(), expected_values) |
1792 | + |
1793 | + config = StringIO("[__main__]\nfoo =") |
1794 | + parser = SchemaConfigParser(schema) |
1795 | + parser.readfp(config) |
1796 | + self.assertRaises(ValueError, parser.values) |
1797 | + |
1798 | + config = StringIO("[__main__]\nfoo = bla") |
1799 | + parser = SchemaConfigParser(schema) |
1800 | + parser.readfp(config) |
1801 | + self.assertRaises(ValueError, parser.values) |
1802 | + |
1803 | + def test_default(self): |
1804 | + opt = IntConfigOption() |
1805 | + self.assertEqual(opt.default, 0) |
1806 | + |
1807 | + |
1808 | +class TestBoolConfigOption(unittest.TestCase): |
1809 | + def test_parse_bool(self): |
1810 | + class MySchema(Schema): |
1811 | + foo = BoolConfigOption() |
1812 | + config = StringIO("[__main__]\nfoo = Yes") |
1813 | + expected_values = {'__main__': {'foo': True}} |
1814 | + schema = MySchema() |
1815 | + parser = SchemaConfigParser(schema) |
1816 | + parser.readfp(config) |
1817 | + self.assertEqual(parser.values(), expected_values) |
1818 | + |
1819 | + config = StringIO("[__main__]\nfoo = tRuE") |
1820 | + parser = SchemaConfigParser(schema) |
1821 | + parser.readfp(config) |
1822 | + self.assertEqual(parser.values(), expected_values) |
1823 | + |
1824 | + config = StringIO("[__main__]\nfoo =") |
1825 | + parser = SchemaConfigParser(schema) |
1826 | + parser.readfp(config) |
1827 | + self.assertRaises(ValueError, parser.values) |
1828 | + |
1829 | + config = StringIO("[__main__]\nfoo = bla") |
1830 | + parser = SchemaConfigParser(schema) |
1831 | + parser.readfp(config) |
1832 | + self.assertRaises(ValueError, parser.values) |
1833 | + |
1834 | + def test_default(self): |
1835 | + opt = BoolConfigOption() |
1836 | + self.assertEqual(opt.default, False) |
1837 | + |
1838 | + |
1839 | +class TestLinesConfigOption(unittest.TestCase): |
1840 | + def test_parse_int_lines(self): |
1841 | + class MySchema(Schema): |
1842 | + foo = LinesConfigOption(item=IntConfigOption()) |
1843 | + |
1844 | + config = StringIO("[__main__]\nfoo = 42\n 43\n 44") |
1845 | + expected_values = {'__main__': {'foo': [42, 43, 44]}} |
1846 | + schema = MySchema() |
1847 | + parser = SchemaConfigParser(schema) |
1848 | + parser.readfp(config) |
1849 | + self.assertEqual(parser.values(), expected_values) |
1850 | + |
1851 | + def test_parse_bool_lines(self): |
1852 | + class MySchema(Schema): |
1853 | + foo = LinesConfigOption(item=BoolConfigOption()) |
1854 | + schema = MySchema() |
1855 | + config = StringIO("[__main__]\nfoo = tRuE\n No\n 0\n 1") |
1856 | + expected_values = {'__main__': {'foo': [True, False, False, True]}} |
1857 | + parser = SchemaConfigParser(schema) |
1858 | + parser.readfp(config) |
1859 | + self.assertEqual(expected_values, parser.values()) |
1860 | + |
1861 | + def test_parse_bool_empty_lines(self): |
1862 | + class MySchema(Schema): |
1863 | + foo = LinesConfigOption(item=BoolConfigOption()) |
1864 | + schema = MySchema() |
1865 | + config = StringIO("[__main__]\nfoo =") |
1866 | + parser = SchemaConfigParser(schema) |
1867 | + parser.readfp(config) |
1868 | + expected_values = {'__main__': {'foo': []}} |
1869 | + self.assertEqual(expected_values, parser.values()) |
1870 | + |
1871 | + def test_parse_bool_invalid_lines(self): |
1872 | + class MySchema(Schema): |
1873 | + foo = LinesConfigOption(item=BoolConfigOption()) |
1874 | + schema = MySchema() |
1875 | + config = StringIO("[__main__]\nfoo = bla") |
1876 | + parser = SchemaConfigParser(schema) |
1877 | + parser.readfp(config) |
1878 | + self.assertRaises(ValueError, parser.values) |
1879 | + |
1880 | + config = StringIO("[__main__]\nfoo = True\n bla") |
1881 | + parser = SchemaConfigParser(schema) |
1882 | + parser.readfp(config) |
1883 | + self.assertRaises(ValueError, parser.values) |
1884 | + |
1885 | + def test_default(self): |
1886 | + opt = LinesConfigOption(item=IntConfigOption()) |
1887 | + self.assertEqual(opt.default, []) |
1888 | + |
1889 | + def test_remove_duplicates(self): |
1890 | + class MySchema(Schema): |
1891 | + foo = LinesConfigOption(item=StringConfigOption(), |
1892 | + remove_duplicates=True) |
1893 | + schema = MySchema() |
1894 | + config = StringIO("[__main__]\nfoo = bla\n blah\n bla") |
1895 | + parser = SchemaConfigParser(schema) |
1896 | + parser.readfp(config) |
1897 | + self.assertEquals({'__main__': {'foo': ['bla', 'blah']}}, |
1898 | + parser.values()) |
1899 | + |
1900 | + def test_remove_dict_duplicates(self): |
1901 | + class MyOtherSchema(Schema): |
1902 | + foo = LinesConfigOption(item=DictConfigOption(), |
1903 | + remove_duplicates=True) |
1904 | + schema = MyOtherSchema() |
1905 | + config = StringIO("[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz") |
1906 | + parser = SchemaConfigParser(schema) |
1907 | + parser.readfp(config) |
1908 | + self.assertEquals({'__main__': {'foo': [{'bar': 'baz'}]}}, |
1909 | + parser.values()) |
1910 | + |
1911 | +class TestTupleConfigOption(unittest.TestCase): |
1912 | + def test_init(self): |
1913 | + opt = TupleConfigOption(2) |
1914 | + self.assertEqual(opt.length, 2) |
1915 | + |
1916 | + def test_init_no_length(self): |
1917 | + opt = TupleConfigOption() |
1918 | + self.assertEqual(opt.length, 0) |
1919 | + self.assertEqual(opt.default, ()) |
1920 | + |
1921 | + def test_parse_no_length(self): |
1922 | + class MySchema(Schema): |
1923 | + foo = TupleConfigOption() |
1924 | + |
1925 | + config = StringIO('[__main__]\nfoo=1,2,3,4') |
1926 | + expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}} |
1927 | + parser = SchemaConfigParser(MySchema()) |
1928 | + parser.readfp(config) |
1929 | + self.assertEqual(parser.values(), expected_values) |
1930 | + |
1931 | + def test_parse_tuple(self): |
1932 | + class MySchema(Schema): |
1933 | + foo = TupleConfigOption(length=4) |
1934 | + config = StringIO('[__main__]\nfoo = 1, 2, 3, 4') |
1935 | + expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}} |
1936 | + schema = MySchema() |
1937 | + parser = SchemaConfigParser(schema) |
1938 | + parser.readfp(config) |
1939 | + self.assertEqual(parser.values(), expected_values) |
1940 | + |
1941 | + config = StringIO('[__main__]\nfoo = 1, 2, 3') |
1942 | + parser = SchemaConfigParser(schema) |
1943 | + parser.readfp(config) |
1944 | + self.assertRaises(ValueError, parser.values) |
1945 | + |
1946 | + config = StringIO('[__main__]\nfoo = ') |
1947 | + parser = SchemaConfigParser(schema) |
1948 | + parser.readfp(config) |
1949 | + self.assertRaises(ValueError, parser.values) |
1950 | + |
1951 | + def test_default(self): |
1952 | + opt = TupleConfigOption(2) |
1953 | + self.assertEqual(opt.default, ()) |
1954 | + |
1955 | + |
1956 | +class TestDictConfigOption(unittest.TestCase): |
1957 | + def test_init(self): |
1958 | + opt = DictConfigOption() |
1959 | + self.assertEqual(opt.spec, {}) |
1960 | + self.assertEqual(opt.strict, False) |
1961 | + |
1962 | + spec = {'a': IntConfigOption(), 'b': BoolConfigOption()} |
1963 | + opt = DictConfigOption(spec) |
1964 | + self.assertEqual(opt.spec, spec) |
1965 | + self.assertEqual(opt.strict, False) |
1966 | + |
1967 | + opt = DictConfigOption(spec, strict=True) |
1968 | + self.assertEqual(opt.spec, spec) |
1969 | + self.assertEqual(opt.strict, True) |
1970 | + |
1971 | + def test_get_extra_sections(self): |
1972 | + class MySchema(Schema): |
1973 | + foo = DictConfigOption(item=DictConfigOption()) |
1974 | + |
1975 | + config = StringIO(""" |
1976 | +[__main__] |
1977 | +foo=dict1 |
1978 | +[dict1] |
1979 | +bar=dict2 |
1980 | +[dict2] |
1981 | +baz=42 |
1982 | +""") |
1983 | + parser = SchemaConfigParser(MySchema()) |
1984 | + parser.readfp(config) |
1985 | + expected = ['dict2'] |
1986 | + |
1987 | + opt = DictConfigOption(item=DictConfigOption()) |
1988 | + extra = opt.get_extra_sections('dict1', parser) |
1989 | + self.assertEqual(extra, expected) |
1990 | + |
1991 | + def test_parse_dict(self): |
1992 | + class MySchema(Schema): |
1993 | + foo = DictConfigOption({'bar': StringConfigOption(), |
1994 | + 'baz': IntConfigOption(), |
1995 | + 'bla': BoolConfigOption(), |
1996 | + }) |
1997 | + config = StringIO("""[__main__] |
1998 | +foo = mydict |
1999 | +[mydict] |
2000 | +bar=baz |
2001 | +baz=42 |
2002 | +bla=Yes |
2003 | +""") |
2004 | + expected_values = { |
2005 | + '__main__': { |
2006 | + 'foo': {'bar': 'baz', 'baz': 42, 'bla': True} |
2007 | + } |
2008 | + } |
2009 | + |
2010 | + schema = MySchema() |
2011 | + parser = SchemaConfigParser(schema) |
2012 | + parser.readfp(config) |
2013 | + self.assertEqual(parser.values(), expected_values) |
2014 | + |
2015 | + def test_parse_raw(self): |
2016 | + class MySchema(Schema): |
2017 | + foo = DictConfigOption({'bar': StringConfigOption(), |
2018 | + 'baz': IntConfigOption(), |
2019 | + 'bla': BoolConfigOption(), |
2020 | + }) |
2021 | + config = StringIO("""[__main__] |
2022 | +foo = mydict |
2023 | +[mydict] |
2024 | +baz=42 |
2025 | +""") |
2026 | + expected = {'bar': '', 'baz': '42', 'bla': 'False'} |
2027 | + |
2028 | + schema = MySchema() |
2029 | + parser = SchemaConfigParser(schema) |
2030 | + parser.readfp(config) |
2031 | + parsed = schema.foo.parse('mydict', parser, True) |
2032 | + self.assertEqual(parsed, expected) |
2033 | + |
2034 | + def test_parse_invalid_key_in_parsed(self): |
2035 | + class MySchema(Schema): |
2036 | + foo = DictConfigOption({'bar': IntConfigOption()}) |
2037 | + |
2038 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbaz=2") |
2039 | + expected_values = {'__main__': {'foo': {'bar': 0, 'baz': '2'}}} |
2040 | + parser = SchemaConfigParser(MySchema()) |
2041 | + parser.readfp(config) |
2042 | + self.assertEqual(parser.values(), expected_values) |
2043 | + |
2044 | + def test_parse_invalid_key_in_spec(self): |
2045 | + class MySchema(Schema): |
2046 | + foo = DictConfigOption({'bar': IntConfigOption(), |
2047 | + 'baz': IntConfigOption(fatal=True)}) |
2048 | + |
2049 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
2050 | + parser = SchemaConfigParser(MySchema()) |
2051 | + parser.readfp(config) |
2052 | + self.assertRaises(ValueError, parser.parse_all) |
2053 | + |
2054 | + def test_default(self): |
2055 | + opt = DictConfigOption({}) |
2056 | + self.assertEqual(opt.default, {}) |
2057 | + |
2058 | + def test_parse_no_strict_missing_args(self): |
2059 | + class MySchema(Schema): |
2060 | + foo = DictConfigOption({'bar': IntConfigOption()}) |
2061 | + |
2062 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]") |
2063 | + expected_values = {'__main__': {'foo': {'bar': 0}}} |
2064 | + parser = SchemaConfigParser(MySchema()) |
2065 | + parser.readfp(config) |
2066 | + self.assertEqual(parser.values(), expected_values) |
2067 | + |
2068 | + def test_parse_no_strict_extra_args(self): |
2069 | + class MySchema(Schema): |
2070 | + foo = DictConfigOption() |
2071 | + |
2072 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
2073 | + expected_values = {'__main__': {'foo': {'bar': '2'}}} |
2074 | + parser = SchemaConfigParser(MySchema()) |
2075 | + parser.readfp(config) |
2076 | + self.assertEqual(parser.values(), expected_values) |
2077 | + |
2078 | + def test_parse_no_strict_with_item(self): |
2079 | + class MySchema(Schema): |
2080 | + foo = DictConfigOption( |
2081 | + item=DictConfigOption( |
2082 | + item=IntConfigOption())) |
2083 | + config = StringIO(""" |
2084 | +[__main__] |
2085 | +foo = mydict |
2086 | +[mydict] |
2087 | +bar = baz |
2088 | +[baz] |
2089 | +wham=42 |
2090 | +""") |
2091 | + expected_values = {'__main__': {'foo': {'bar': {'wham': 42}}}} |
2092 | + parser = SchemaConfigParser(MySchema()) |
2093 | + parser.readfp(config) |
2094 | + self.assertEqual(parser.values(), expected_values) |
2095 | + |
2096 | + def test_parse_strict(self): |
2097 | + class MySchema(Schema): |
2098 | + spec = {'bar': IntConfigOption()} |
2099 | + foo = DictConfigOption(spec, strict=True) |
2100 | + |
2101 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
2102 | + expected_values = {'__main__': {'foo': {'bar': 2}}} |
2103 | + parser = SchemaConfigParser(MySchema()) |
2104 | + parser.readfp(config) |
2105 | + self.assertEqual(parser.values(), expected_values) |
2106 | + |
2107 | + def test_parse_strict_missing_vars(self): |
2108 | + class MySchema(Schema): |
2109 | + spec = {'bar': IntConfigOption(), |
2110 | + 'baz': IntConfigOption()} |
2111 | + foo = DictConfigOption(spec, strict=True) |
2112 | + |
2113 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2") |
2114 | + expected_values = {'__main__': {'foo': {'bar': 2, 'baz': 0}}} |
2115 | + parser = SchemaConfigParser(MySchema()) |
2116 | + parser.readfp(config) |
2117 | + self.assertEqual(parser.values(), expected_values) |
2118 | + |
2119 | + def test_parse_strict_extra_vars(self): |
2120 | + class MySchema(Schema): |
2121 | + spec = {'bar': IntConfigOption()} |
2122 | + foo = DictConfigOption(spec, strict=True) |
2123 | + |
2124 | + config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3") |
2125 | + parser = SchemaConfigParser(MySchema()) |
2126 | + parser.readfp(config) |
2127 | + self.assertRaises(ValueError, parser.parse_all) |
2128 | + |
2129 | + |
2130 | +class TestLinesOfDictConfigOption(unittest.TestCase): |
2131 | + def test_parse_lines_of_dict(self): |
2132 | + class MySchema(Schema): |
2133 | + foo = LinesConfigOption( |
2134 | + DictConfigOption({'bar': StringConfigOption(), |
2135 | + 'baz': IntConfigOption(), |
2136 | + 'bla': BoolConfigOption(), |
2137 | + })) |
2138 | + config = StringIO("""[__main__] |
2139 | +foo = mylist0 |
2140 | + mylist1 |
2141 | +[mylist0] |
2142 | +bar=baz |
2143 | +baz=42 |
2144 | +bla=Yes |
2145 | +[mylist1] |
2146 | +bar=zort |
2147 | +baz=123 |
2148 | +bla=0 |
2149 | +""") |
2150 | + expected_values = { |
2151 | + '__main__': {'foo': [{'bar': 'baz', 'baz': 42, 'bla': True}, |
2152 | + {'bar': 'zort', 'baz': 123, 'bla': False}, |
2153 | + ]}} |
2154 | + |
2155 | + schema = MySchema() |
2156 | + parser = SchemaConfigParser(schema) |
2157 | + parser.readfp(config) |
2158 | + self.assertEqual(parser.values(), expected_values) |
2159 | + |
2160 | + |
2161 | +class TestDictWithDicts(unittest.TestCase): |
2162 | + def test_parse_dict_with_dicts(self): |
2163 | + innerspec = {'bar': StringConfigOption(), |
2164 | + 'baz': IntConfigOption(), |
2165 | + 'bla': BoolConfigOption(), |
2166 | + } |
2167 | + spec = {'name': StringConfigOption(), |
2168 | + 'size': IntConfigOption(), |
2169 | + 'options': DictConfigOption(innerspec)} |
2170 | + class MySchema(Schema): |
2171 | + foo = DictConfigOption(spec) |
2172 | + config = StringIO("""[__main__] |
2173 | +foo = outerdict |
2174 | +[outerdict] |
2175 | +options = innerdict |
2176 | +[innerdict] |
2177 | +bar = something |
2178 | +baz = 42 |
2179 | +""") |
2180 | + expected_values = { |
2181 | + '__main__': {'foo': {'name': '', 'size': 0, |
2182 | + 'options': {'bar': 'something', 'baz': 42, |
2183 | + 'bla': False}}}} |
2184 | + schema = MySchema() |
2185 | + parser = SchemaConfigParser(schema) |
2186 | + parser.readfp(config) |
2187 | + self.assertEqual(parser.values(), expected_values) |
2188 | + |
2189 | + |
2190 | +class TestListOfTuples(unittest.TestCase): |
2191 | + def setUp(self): |
2192 | + class MySchema(Schema): |
2193 | + foo = LinesConfigOption(item=TupleConfigOption(length=3)) |
2194 | + schema = MySchema() |
2195 | + self.parser = SchemaConfigParser(schema) |
2196 | + |
2197 | + def test_parse_list_of_tuples(self): |
2198 | + config = StringIO('[__main__]\nfoo = a, b, c\n d, e, f') |
2199 | + expected_values = { |
2200 | + '__main__': {'foo': [('a', 'b', 'c'), ('d', 'e', 'f')]}} |
2201 | + self.parser.readfp(config) |
2202 | + self.assertEqual(self.parser.values(), expected_values) |
2203 | + |
2204 | + def test_parse_wrong_tuple_size(self): |
2205 | + config = StringIO('[__main__]\nfoo = a, b, c\n d, e') |
2206 | + self.parser.readfp(config) |
2207 | + self.assertRaises(ValueError, self.parser.values) |
2208 | + |
2209 | + def test_parse_empty_tuple(self): |
2210 | + config = StringIO('[__main__]\nfoo=()') |
2211 | + expected_values = {'__main__': {'foo': [()]}} |
2212 | + self.parser.readfp(config) |
2213 | + self.assertEqual(self.parser.values(), expected_values) |
2214 | + |
2215 | |
2216 | === modified file 'tests/pyschema/test_schemaconfig.py' |
2217 | --- tests/pyschema/test_schemaconfig.py 2010-12-18 21:12:24 +0000 |
2218 | +++ tests/pyschema/test_schemaconfig.py 2010-12-19 19:01:37 +0000 |
2219 | @@ -21,10 +21,17 @@ |
2220 | |
2221 | from mock import patch, Mock |
2222 | |
2223 | -from configglue.pyschema import ConfigOption, ConfigSection, schemaconfigglue, configglue |
2224 | -from configglue.pyschema.options import IntConfigOption |
2225 | +from configglue.utils import ( |
2226 | + configglue, |
2227 | + schemaconfigglue, |
2228 | +) |
2229 | from configglue.pyschema.parser import SchemaConfigParser |
2230 | -from configglue.pyschema.schema import Schema |
2231 | +from configglue.pyschema.schema import ( |
2232 | + ConfigOption, |
2233 | + ConfigSection, |
2234 | + IntConfigOption, |
2235 | + Schema, |
2236 | +) |
2237 | |
2238 | |
2239 | class TestConfigOption(unittest.TestCase): |
2240 | @@ -183,8 +190,8 @@ |
2241 | |
2242 | |
2243 | class ConfigglueTestCase(unittest.TestCase): |
2244 | - @patch('configglue.pyschema.SchemaConfigParser') |
2245 | - @patch('configglue.pyschema.schemaconfigglue') |
2246 | + @patch('configglue.pyschema.parser.SchemaConfigParser') |
2247 | + @patch('configglue.utils.schemaconfigglue') |
2248 | def test_configglue(self, mock_schemaconfigglue, mock_schema_parser): |
2249 | # prepare mocks |
2250 | expected_schema_parser = Mock() |
2251 | @@ -216,8 +223,8 @@ |
2252 | self.assertEqual(glue.options, expected_options) |
2253 | self.assertEqual(glue.args, expected_args) |
2254 | |
2255 | - @patch('configglue.pyschema.SchemaConfigParser') |
2256 | - @patch('configglue.pyschema.schemaconfigglue') |
2257 | + @patch('configglue.utils.SchemaConfigParser') |
2258 | + @patch('configglue.utils.schemaconfigglue') |
2259 | def test_configglue(self, mock_schemaconfigglue, mock_schema_parser): |
2260 | # prepare mocks |
2261 | expected_schema_parser = Mock() |
2262 | @@ -250,9 +257,9 @@ |
2263 | self.assertEqual(glue.options, expected_options) |
2264 | self.assertEqual(glue.args, expected_args) |
2265 | |
2266 | - @patch('configglue.pyschema.OptionParser') |
2267 | - @patch('configglue.pyschema.SchemaConfigParser') |
2268 | - @patch('configglue.pyschema.schemaconfigglue') |
2269 | + @patch('configglue.utils.OptionParser') |
2270 | + @patch('configglue.utils.SchemaConfigParser') |
2271 | + @patch('configglue.utils.schemaconfigglue') |
2272 | def test_configglue_with_usage(self, mock_schemaconfigglue, |
2273 | mock_schema_parser, mock_option_parser): |
2274 | # prepare mocks |
You rock.