Merge lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue

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
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.
Revision history for this message
John Lenton (chipaca) wrote :

You rock.

review: Approve

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

Subscribers

People subscribed via source and target branches