← Back to team overview

configglue team mailing list archive

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

 

Ricardo Kirkner has proposed merging lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue.

Requested reviews:
  Configglue developers (configglue)


This branch refactors some imports to remove circular dependencies, and cleans up a bit the project structure.
-- 
https://code.launchpad.net/~ricardokirkner/configglue/remove-circular-dependencies/+merge/44167
Your team Configglue developers is requested to review the proposed merge of lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue.
=== modified file 'configglue/inischema/glue.py'
--- configglue/inischema/glue.py	2010-08-05 11:55:13 +0000
+++ configglue/inischema/glue.py	2010-12-19 19:01:37 +0000
@@ -19,9 +19,13 @@
 """
 from __future__ import absolute_import
 
-from configglue.pyschema import schemaconfigglue, ini2schema
-
-__all__ = ('configglue',)
+from configglue.utils import schemaconfigglue, ini2schema
+
+
+__all__ = [
+    'configglue',
+]
+
 
 def configglue(fileobj, *filenames, **kwargs):
     args = kwargs.pop('args', None)

=== modified file 'configglue/pyschema/__init__.py'
--- configglue/pyschema/__init__.py	2010-12-18 21:12:24 +0000
+++ configglue/pyschema/__init__.py	2010-12-19 19:01:37 +0000
@@ -15,267 +15,5 @@
 # 
 ###############################################################################
 
-import __builtin__
-from optparse import OptionParser
-from collections import namedtuple
-import sys
-
-# XXX: more imports at bottom (we probably want to fix this)
-
-SchemaGlue = namedtuple("SchemaGlue", "schema_parser option_parser options args")
-IniGlue = namedtuple("IniGlue", " option_parser options args")
-
-def ini2schema(fd, p=None):
-    """
-    Turn a fd that refers to a INI-style schema definition into a
-    SchemaConfigParser object
-
-    @param fd: file-like object to read the schema from
-    @param p: a parser to use. If not set, uses AttributedConfigParser
-    """
-    if p is None:
-        p = AttributedConfigParser()
-    p.readfp(fd)
-    p.parse_all()
-
-    parser2option = {'unicode': options.StringConfigOption,
-                     'int': options.IntConfigOption,
-                     'bool': options.BoolConfigOption,
-                     'lines': options.LinesConfigOption}
-
-    class MySchema(Schema):
-        pass
-
-    for section_name in p.sections():
-        if section_name == '__main__':
-            section = MySchema
-        else:
-            section = ConfigSection()
-            setattr(MySchema, section_name, section)
-        for option_name in p.options(section_name):
-            option = p.get(section_name, option_name)
-
-            parser = option.attrs.pop('parser', 'unicode')
-            parser_args = option.attrs.pop('parser.args', '').split()
-            parser_fun = getattr(parsers, parser, None)
-            if parser_fun is None:
-                parser_fun = getattr(__builtin__, parser, None)
-            if parser_fun is None:
-                parser_fun = lambda x: x
-
-            attrs = {}
-            option_help = option.attrs.pop('help', None)
-            if option_help is not None:
-                attrs['help'] = option_help
-            if not option.is_empty:
-                attrs['default'] = parser_fun(option.value, *parser_args)
-            option_action = option.attrs.pop('action', None)
-            if option_action is not None:
-                attrs['action'] = option_action
-
-            klass = parser2option.get(parser, options.StringConfigOption)
-            if parser == 'lines':
-                instance = klass(options.StringConfigOption(), **attrs)
-            else:
-                instance = klass(**attrs)
-            setattr(section, option_name, instance)
-
-    return SchemaConfigParser(MySchema())
-
-
-def schemaconfigglue(parser, op=None, argv=None):
-    """Populate an OptionParser with options and defaults taken from a
-    fully loaded SchemaConfigParser.
-    """
-
-    def long_name(option):
-        if option.section.name == '__main__':
-            return option.name
-        return option.section.name + '_' + option.name
-
-    def opt_name(option):
-        return long_name(option).replace('-', '_')
-
-    if op is None:
-        op = OptionParser()
-    if argv is None:
-        argv = sys.argv[1:]
-    schema = parser.schema
-
-    for section in schema.sections():
-        if section.name == '__main__':
-            og = op
-        else:
-            og = op.add_option_group(section.name)
-        for option in section.options():
-            kwargs = {}
-            if option.help:
-                kwargs['help'] = option.help
-            kwargs['default'] = parser.get(section.name, option.name)
-            kwargs['action'] = option.action
-            og.add_option('--' + long_name(option), **kwargs)
-    options, args = op.parse_args(argv)
-
-    for section in schema.sections():
-        for option in section.options():
-            value = getattr(options, opt_name(option))
-            if parser.get(section.name, option.name) != value:
-                # the value has been overridden by an argument;
-                # update it.
-                parser.set(section.name, option.name, value)
-
-    return IniGlue(op, options, args)
-
-def super_vars(obj):
-    """An extended version of vars() that walks all base classes."""
-    items = {}
-    if hasattr(obj, '__mro__'):
-        bases = map(vars, obj.__mro__)
-        map(items.update, bases)
-    else:
-        items = vars(obj)
-    return items
-
-NO_DEFAULT = object()
-
-
-class ConfigOption(object):
-    """Base class for Config Options.
-
-    ConfigOptions are never bound to a particular conguration file, and
-    simply describe one particular available option.
-
-    They also know how to parse() the content of a config file in to the right
-    type of object.
-
-    If self.raw == True, then variable interpolation will not be carried out
-    for this config option.
-
-    If self.require_parser == True, then the parse() method will have a second
-    argument, parser, that should receive the whole SchemaConfigParser to
-    do the parsing.  This is needed for config options that need to look at
-    other parts of the config file to be able to carry out their parsing,
-    like DictConfigOptions.
-
-    If self.fatal == True, SchemaConfigParser's parse_all will raise an
-    exception if no value for this option is provided in the configuration
-    file.  Otherwise, the self.default value will be used if the option is
-    omitted.
-
-    In runtime, after instantiating the Schema, each ConfigOption will also
-    know its own name and to which section it belongs.
-    """
-
-    require_parser = False
-
-    def __init__(self, name='', raw=False, default=NO_DEFAULT, fatal=False, help='',
-                 section=None, action='store'):
-        self.name = name
-        self.raw = raw
-        self.fatal = fatal
-        if default is NO_DEFAULT:
-            default = self._get_default()
-        self.default = default
-        self.help = help
-        self.section = section
-        self.action = action
-
-    def __eq__(self, other):
-        try:
-            equal = (self.name == other.name and
-                     self.raw == other.raw and
-                     self.fatal == other.fatal and
-                     self.default == other.default and
-                     self.help == other.help)
-            if self.section is not None and other.section is not None:
-                # only test for section name to avoid recursion
-                equal &= self.section.name == other.section.name
-            else:
-                equal &= (self.section is None and other.section is None)
-        except AttributeError:
-            equal = False
-
-        return equal
-
-    def __repr__(self):
-        extra = ' raw' if self.raw else ''
-        extra += ' fatal' if self.fatal else ''
-        section = self.section.name if self.section is not None else None
-        if section is not None:
-            name = " %s.%s" % (section, self.name)
-        elif self.name:
-            name = " %s" % self.name
-        else:
-            name = ''
-        value = "<ConfigOption%s%s>" % (name, extra)
-        return value
-
-    def _get_default(self):
-        return None
-
-    def parse(self, value):
-        raise NotImplementedError()
-
-
-class ConfigSection(object):
-    """A group of options.
-
-    This class is just a bag you can dump ConfigOptions in.
-
-    After instantiating the Schema, each ConfigSection will know its own
-    name.
-    """
-    def __init__(self, name=''):
-        self.name = name
-
-    def __eq__(self, other):
-        return (self.name == other.name and
-                self.options() == other.options())
-
-    def __repr__(self):
-        if self.name:
-            name = " %s" % self.name
-        else:
-            name = ''
-        value = "<ConfigSection%s>" % name
-        return value
-
-    def has_option(self, name):
-        """Return True if a ConfigOption with the given name is available"""
-        opt = getattr(self, name, None)
-        return isinstance(opt, ConfigOption)
-
-    def option(self, name):
-        """Return a ConfigOption by name"""
-        opt = getattr(self, name, None)
-        assert opt is not None, "Invalid ConfigOption name '%s'" % name
-        return opt
-
-    def options(self):
-        """Return a list of all available ConfigOptions within this section"""
-        return [getattr(self, att) for att in vars(self)
-                if isinstance(getattr(self, att), ConfigOption)]
-
-
-# usability tweak -- put everything in the base namespace to make import lines
-# shorter
-from options import (BoolConfigOption, DictConfigOption, IntConfigOption,
-    LinesConfigOption, StringConfigOption, TupleConfigOption)
-from parser import SchemaConfigParser
-from schema import Schema
-
-def configglue(schema_class, configs, usage=None):
-    scp = SchemaConfigParser(schema_class())
-    scp.read(configs)
-    if usage is not None:
-        op = OptionParser(usage=usage)
-    else:
-        op = None
-    parser, opts, args = schemaconfigglue(scp, op=op)
-    is_valid, reasons = scp.is_valid(report=True)
-    if not is_valid:
-        parser.error(reasons[0])
-    return SchemaGlue(scp, parser, opts, args)
-
-# circular import avoidance
-from configglue.inischema import AttributedConfigParser, parsers
+from configglue.pyschema.parser import *
+from configglue.pyschema.schema import *

=== removed file 'configglue/pyschema/options.py'
--- configglue/pyschema/options.py	2010-08-04 21:00:09 +0000
+++ configglue/pyschema/options.py	1970-01-01 00:00:00 +0000
@@ -1,245 +0,0 @@
-###############################################################################
-# 
-# configglue -- glue for your apps' configuration
-# 
-# A library for simple, DRY configuration of applications
-# 
-# (C) 2009--2010 by Canonical Ltd.
-# originally by John R. Lenton <john.lenton@xxxxxxxxxxxxx>
-# incorporating schemaconfig as configglue.pyschema
-# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@xxxxxxxxxxxxx>
-# 
-# Released under the BSD License (see the file LICENSE)
-# 
-# For bug reports, support, and new releases: http://launchpad.net/configglue
-# 
-###############################################################################
-
-from configglue.pyschema import ConfigOption, NO_DEFAULT
-
-
-class BoolConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into a bool"""
-
-    def _get_default(self):
-        return False
-
-    def parse(self, value, raw=False):
-        if raw:
-            return value
-
-        if value.lower() in ['y', '1', 'yes', 'on', 'true']:
-            return True
-        elif value.lower() in ['n', '0', 'no', 'off', 'false']:
-            return False
-        else:
-            raise ValueError("Unable to determine boolosity of %r" % value)
-
-
-class IntConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into an int"""
-
-    def _get_default(self):
-        return 0
-
-    def parse(self, value, raw=False):
-        if raw:
-            return value
-
-        return int(value)
-
-
-class LinesConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into a list of objects
-
-    All items in the list need to be of the same type.  The 'item' constructor
-    argument determines the type of the list items. item should be another
-    child of ConfigOption.
-
-    self.require_parser will be True if the item provided in turn has
-    require_parser == True.
-
-    if remove_duplicates == True, duplicate elements in the lines will be
-    removed.  Only the first occurrence of any item will be kept,
-    otherwise the general order of the list will be preserved.
-    """
-
-    def _get_default(self):
-        return []
-
-    def parse(self, value, parser=None, raw=False):
-        def _parse_item(value):
-            if self.require_parser:
-                value = self.item.parse(value, parser=parser, raw=raw)
-            else:
-                value = self.item.parse(value, raw=raw)
-            return value
-        items = [_parse_item(x) for x in value.split('\n') if len(x)]
-        if self.remove_duplicates:
-            filtered_items = []
-            for item in items:
-                if not item in filtered_items:
-                    filtered_items.append(item)
-            items = filtered_items
-        return items
-
-    def __init__(self, item, raw=False, default=NO_DEFAULT, fatal=False,
-        help='', action='store', remove_duplicates=False):
-        super(LinesConfigOption, self).__init__(raw=raw, default=default,
-            fatal=fatal, help=help, action=action)
-        self.item = item
-        self.require_parser = item.require_parser
-        self.raw = item.raw
-        self.remove_duplicates = remove_duplicates
-
-class StringConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into a string.
-
-    If null==True, a value of 'None' will be parsed in to None instead of
-    just leaving it as the string 'None'.
-    """
-
-    def _get_default(self):
-        return '' if not self.null else None
-
-    def parse(self, value, raw=False):
-        if raw:
-            return value
-
-        return unicode(value)
-
-    def __init__(self, raw=False, default=NO_DEFAULT, fatal=False, null=False,
-                 help='', action='store'):
-        self.null = null
-        super(StringConfigOption, self).__init__(raw=raw, default=default,
-            fatal=fatal, help=help, action=action)
-
-
-class TupleConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into a fixed-size tuple of strings.
-
-    The number of items in the tuple should be specified with the 'length'
-    constructor argument.
-    """
-
-    def __init__(self, length=0, raw=False, default=NO_DEFAULT, fatal=False,
-                 help='', action='store'):
-        super(TupleConfigOption, self).__init__(raw=raw, default=default,
-            fatal=fatal, help=help, action=action)
-        self.length = length
-
-    def _get_default(self):
-        return ()
-
-    def parse(self, value, raw=False):
-        parts = [part.strip() for part in value.split(',')]
-        if parts == ['()']:
-            result = ()
-        elif self.length:
-            # length is not 0, so length validation
-            if len(parts) == self.length:
-                result = tuple(parts)
-            else:
-                raise ValueError("Tuples need to be %d items long" % self.length)
-        else:
-            result = tuple(parts)
-            # length is 0, so no length validation
-        return result
-
-
-class DictConfigOption(ConfigOption):
-    """A ConfigOption that is parsed into a dictionary.
-
-    In the configuration file you'll need to specify the name of a section,
-    and all that section's items will be parsed as a dictionary.
-
-    The available keys for the dict are specified with the 'spec' constructor
-    argument, that should be in turn a dictionary.  spec's keys are the
-    available keys for the config file, and spec's values should be
-    ConfigOptions that will be used to parse the values in the config file.
-    """
-    require_parser = True
-
-    def __init__(self, spec=None, strict=False, raw=False,
-                 default=NO_DEFAULT, fatal=False, help='', action='store',
-                 item=None):
-        if spec is None:
-            spec = {}
-        if item is None:
-            item = StringConfigOption()
-        self.spec = spec
-        self.strict = strict
-        self.item = item
-        super(DictConfigOption, self).__init__(raw=raw, default=default,
-            fatal=fatal, help=help, action=action)
-
-    def _get_default(self):
-        default = {}
-        for key, value in self.spec.items():
-            default[key] = value.default
-        return default
-
-    def parse(self, section, parser=None, raw=False):
-        parsed = dict(parser.items(section))
-        result = {}
-
-        # parse config items according to spec
-        for key, value in parsed.items():
-            if self.strict and not key in self.spec:
-                raise ValueError("Invalid key %s in section %s" % (key,
-                                                                   section))
-            option = self.spec.get(key, None)
-            if option is None:
-                # option not part of spec, but we are in non-strict mode
-                # parse it using the default item parser
-                option = self.item
-
-            # parse option
-            kwargs = {}
-            if option.require_parser:
-                kwargs['parser'] = parser
-            if not raw:
-                value = option.parse(value, **kwargs)
-            result[key] = value
-
-        # fill in missing items with default values
-        for key in self.spec:
-            if not key in parsed:
-                option = self.spec[key]
-                if option.fatal:
-                    raise ValueError("No option '%s' in section '%s'" %
-                        (key, section))
-                else:
-                    if not raw:
-                        value = option.default
-                    else:
-                        value = unicode(option.default)
-                    result[key] = value
-        return result
-
-    def get_extra_sections(self, section, parser):
-        sections = []
-        for option in parser.options(section):
-            option_obj = self.spec.get(option, self.item)
-            is_dict_item = isinstance(option_obj, DictConfigOption)
-            is_dict_lines_item = (hasattr(option_obj, 'item') and
-                isinstance(option_obj.item, DictConfigOption))
-
-            if is_dict_item:
-                base = option_obj
-            elif is_dict_lines_item:
-                base = option_obj.item
-            else:
-                continue
-
-            value = parser.get(section, option, parse=False)
-            names = value.split()
-            sections.extend(names)
-
-            # recurse
-            for name in names:
-                extra = base.get_extra_sections(name, parser)
-                sections.extend(extra)
-
-        return sections
-

=== modified file 'configglue/pyschema/parser.py'
--- configglue/pyschema/parser.py	2010-07-31 01:15:59 +0000
+++ configglue/pyschema/parser.py	2010-12-19 19:01:37 +0000
@@ -21,12 +21,21 @@
 import os
 import string
 
-from ConfigParser import (NoSectionError, DEFAULTSECT,
-    InterpolationMissingOptionError, NoOptionError,
-    ConfigParser as BaseConfigParser)
-
-from configglue.pyschema.options import DictConfigOption
-
+from ConfigParser import (
+    DEFAULTSECT,
+    ConfigParser as BaseConfigParser,
+    InterpolationMissingOptionError,
+    NoOptionError,
+    NoSectionError,
+)
+
+from configglue.pyschema.schema import DictConfigOption
+
+
+__all__ = [
+    'SchemaValidationError',
+    'SchemaConfigParser',
+]
 
 CONFIG_FILE_ENCODING = 'utf-8'
 

=== modified file 'configglue/pyschema/schema.py'
--- configglue/pyschema/schema.py	2010-08-06 19:29:19 +0000
+++ configglue/pyschema/schema.py	2010-12-19 19:01:37 +0000
@@ -17,12 +17,36 @@
 
 from copy import deepcopy
 
-from configglue.pyschema import ConfigOption, ConfigSection, super_vars
-from configglue.pyschema.options import LinesConfigOption, StringConfigOption
-
+
+__all__ = [
+    'super_vars',
+    'BoolConfigOption',
+    'ConfigOption',
+    'ConfigSection',
+    'DictConfigOption',
+    'IntConfigOption',
+    'LinesConfigOption',
+    'Schema',
+    'StringConfigOption',
+    'TupleConfigOption',
+]
+
+NO_DEFAULT = object()
 
 _internal = object.__dict__.keys() + ['__module__']
 
+
+def super_vars(obj):
+    """An extended version of vars() that walks all base classes."""
+    items = {}
+    if hasattr(obj, '__mro__'):
+        bases = map(vars, obj.__mro__)
+        map(items.update, bases)
+    else:
+        items = vars(obj)
+    return items
+
+
 class Schema(object):
     """A complete description of a system configuration.
 
@@ -123,3 +147,347 @@
         return options
 
 
+class ConfigSection(object):
+    """A group of options.
+
+    This class is just a bag you can dump ConfigOptions in.
+
+    After instantiating the Schema, each ConfigSection will know its own
+    name.
+    """
+    def __init__(self, name=''):
+        self.name = name
+
+    def __eq__(self, other):
+        return (self.name == other.name and
+                self.options() == other.options())
+
+    def __repr__(self):
+        if self.name:
+            name = " %s" % self.name
+        else:
+            name = ''
+        value = "<ConfigSection%s>" % name
+        return value
+
+    def has_option(self, name):
+        """Return True if a ConfigOption with the given name is available"""
+        opt = getattr(self, name, None)
+        return isinstance(opt, ConfigOption)
+
+    def option(self, name):
+        """Return a ConfigOption by name"""
+        opt = getattr(self, name, None)
+        assert opt is not None, "Invalid ConfigOption name '%s'" % name
+        return opt
+
+    def options(self):
+        """Return a list of all available ConfigOptions within this section"""
+        return [getattr(self, att) for att in vars(self)
+                if isinstance(getattr(self, att), ConfigOption)]
+
+
+class ConfigOption(object):
+    """Base class for Config Options.
+
+    ConfigOptions are never bound to a particular conguration file, and
+    simply describe one particular available option.
+
+    They also know how to parse() the content of a config file in to the right
+    type of object.
+
+    If self.raw == True, then variable interpolation will not be carried out
+    for this config option.
+
+    If self.require_parser == True, then the parse() method will have a second
+    argument, parser, that should receive the whole SchemaConfigParser to
+    do the parsing.  This is needed for config options that need to look at
+    other parts of the config file to be able to carry out their parsing,
+    like DictConfigOptions.
+
+    If self.fatal == True, SchemaConfigParser's parse_all will raise an
+    exception if no value for this option is provided in the configuration
+    file.  Otherwise, the self.default value will be used if the option is
+    omitted.
+
+    In runtime, after instantiating the Schema, each ConfigOption will also
+    know its own name and to which section it belongs.
+    """
+
+    require_parser = False
+
+    def __init__(self, name='', raw=False, default=NO_DEFAULT, fatal=False, help='',
+                 section=None, action='store'):
+        self.name = name
+        self.raw = raw
+        self.fatal = fatal
+        if default is NO_DEFAULT:
+            default = self._get_default()
+        self.default = default
+        self.help = help
+        self.section = section
+        self.action = action
+
+    def __eq__(self, other):
+        try:
+            equal = (self.name == other.name and
+                     self.raw == other.raw and
+                     self.fatal == other.fatal and
+                     self.default == other.default and
+                     self.help == other.help)
+            if self.section is not None and other.section is not None:
+                # only test for section name to avoid recursion
+                equal &= self.section.name == other.section.name
+            else:
+                equal &= (self.section is None and other.section is None)
+        except AttributeError:
+            equal = False
+
+        return equal
+
+    def __repr__(self):
+        extra = ' raw' if self.raw else ''
+        extra += ' fatal' if self.fatal else ''
+        section = self.section.name if self.section is not None else None
+        if section is not None:
+            name = " %s.%s" % (section, self.name)
+        elif self.name:
+            name = " %s" % self.name
+        else:
+            name = ''
+        value = "<ConfigOption%s%s>" % (name, extra)
+        return value
+
+    def _get_default(self):
+        return None
+
+    def parse(self, value):
+        raise NotImplementedError()
+
+
+class BoolConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into a bool"""
+
+    def _get_default(self):
+        return False
+
+    def parse(self, value, raw=False):
+        if raw:
+            return value
+
+        if value.lower() in ['y', '1', 'yes', 'on', 'true']:
+            return True
+        elif value.lower() in ['n', '0', 'no', 'off', 'false']:
+            return False
+        else:
+            raise ValueError("Unable to determine boolosity of %r" % value)
+
+
+class IntConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into an int"""
+
+    def _get_default(self):
+        return 0
+
+    def parse(self, value, raw=False):
+        if raw:
+            return value
+
+        return int(value)
+
+
+class LinesConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into a list of objects
+
+    All items in the list need to be of the same type.  The 'item' constructor
+    argument determines the type of the list items. item should be another
+    child of ConfigOption.
+
+    self.require_parser will be True if the item provided in turn has
+    require_parser == True.
+
+    if remove_duplicates == True, duplicate elements in the lines will be
+    removed.  Only the first occurrence of any item will be kept,
+    otherwise the general order of the list will be preserved.
+    """
+
+    def _get_default(self):
+        return []
+
+    def parse(self, value, parser=None, raw=False):
+        def _parse_item(value):
+            if self.require_parser:
+                value = self.item.parse(value, parser=parser, raw=raw)
+            else:
+                value = self.item.parse(value, raw=raw)
+            return value
+        items = [_parse_item(x) for x in value.split('\n') if len(x)]
+        if self.remove_duplicates:
+            filtered_items = []
+            for item in items:
+                if not item in filtered_items:
+                    filtered_items.append(item)
+            items = filtered_items
+        return items
+
+    def __init__(self, item, raw=False, default=NO_DEFAULT, fatal=False,
+        help='', action='store', remove_duplicates=False):
+        super(LinesConfigOption, self).__init__(raw=raw, default=default,
+            fatal=fatal, help=help, action=action)
+        self.item = item
+        self.require_parser = item.require_parser
+        self.raw = item.raw
+        self.remove_duplicates = remove_duplicates
+
+
+class StringConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into a string.
+
+    If null==True, a value of 'None' will be parsed in to None instead of
+    just leaving it as the string 'None'.
+    """
+
+    def _get_default(self):
+        return '' if not self.null else None
+
+    def parse(self, value, raw=False):
+        if raw:
+            return value
+
+        return unicode(value)
+
+    def __init__(self, raw=False, default=NO_DEFAULT, fatal=False, null=False,
+                 help='', action='store'):
+        self.null = null
+        super(StringConfigOption, self).__init__(raw=raw, default=default,
+            fatal=fatal, help=help, action=action)
+
+
+class TupleConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into a fixed-size tuple of strings.
+
+    The number of items in the tuple should be specified with the 'length'
+    constructor argument.
+    """
+
+    def __init__(self, length=0, raw=False, default=NO_DEFAULT, fatal=False,
+                 help='', action='store'):
+        super(TupleConfigOption, self).__init__(raw=raw, default=default,
+            fatal=fatal, help=help, action=action)
+        self.length = length
+
+    def _get_default(self):
+        return ()
+
+    def parse(self, value, raw=False):
+        parts = [part.strip() for part in value.split(',')]
+        if parts == ['()']:
+            result = ()
+        elif self.length:
+            # length is not 0, so length validation
+            if len(parts) == self.length:
+                result = tuple(parts)
+            else:
+                raise ValueError("Tuples need to be %d items long" % self.length)
+        else:
+            result = tuple(parts)
+            # length is 0, so no length validation
+        return result
+
+
+class DictConfigOption(ConfigOption):
+    """A ConfigOption that is parsed into a dictionary.
+
+    In the configuration file you'll need to specify the name of a section,
+    and all that section's items will be parsed as a dictionary.
+
+    The available keys for the dict are specified with the 'spec' constructor
+    argument, that should be in turn a dictionary.  spec's keys are the
+    available keys for the config file, and spec's values should be
+    ConfigOptions that will be used to parse the values in the config file.
+    """
+    require_parser = True
+
+    def __init__(self, spec=None, strict=False, raw=False,
+                 default=NO_DEFAULT, fatal=False, help='', action='store',
+                 item=None):
+        if spec is None:
+            spec = {}
+        if item is None:
+            item = StringConfigOption()
+        self.spec = spec
+        self.strict = strict
+        self.item = item
+        super(DictConfigOption, self).__init__(raw=raw, default=default,
+            fatal=fatal, help=help, action=action)
+
+    def _get_default(self):
+        default = {}
+        for key, value in self.spec.items():
+            default[key] = value.default
+        return default
+
+    def parse(self, section, parser=None, raw=False):
+        parsed = dict(parser.items(section))
+        result = {}
+
+        # parse config items according to spec
+        for key, value in parsed.items():
+            if self.strict and not key in self.spec:
+                raise ValueError("Invalid key %s in section %s" % (key,
+                                                                   section))
+            option = self.spec.get(key, None)
+            if option is None:
+                # option not part of spec, but we are in non-strict mode
+                # parse it using the default item parser
+                option = self.item
+
+            # parse option
+            kwargs = {}
+            if option.require_parser:
+                kwargs['parser'] = parser
+            if not raw:
+                value = option.parse(value, **kwargs)
+            result[key] = value
+
+        # fill in missing items with default values
+        for key in self.spec:
+            if not key in parsed:
+                option = self.spec[key]
+                if option.fatal:
+                    raise ValueError("No option '%s' in section '%s'" %
+                        (key, section))
+                else:
+                    if not raw:
+                        value = option.default
+                    else:
+                        value = unicode(option.default)
+                    result[key] = value
+        return result
+
+    def get_extra_sections(self, section, parser):
+        sections = []
+        for option in parser.options(section):
+            option_obj = self.spec.get(option, self.item)
+            is_dict_item = isinstance(option_obj, DictConfigOption)
+            is_dict_lines_item = (hasattr(option_obj, 'item') and
+                isinstance(option_obj.item, DictConfigOption))
+
+            if is_dict_item:
+                base = option_obj
+            elif is_dict_lines_item:
+                base = option_obj.item
+            else:
+                continue
+
+            value = parser.get(section, option, parse=False)
+            names = value.split()
+            sections.extend(names)
+
+            # recurse
+            for name in names:
+                extra = base.get_extra_sections(name, parser)
+                sections.extend(extra)
+
+        return sections
+

=== added file 'configglue/utils.py'
--- configglue/utils.py	1970-01-01 00:00:00 +0000
+++ configglue/utils.py	2010-12-19 19:01:37 +0000
@@ -0,0 +1,162 @@
+###############################################################################
+# 
+# configglue -- glue for your apps' configuration
+# 
+# A library for simple, DRY configuration of applications
+# 
+# (C) 2009--2010 by Canonical Ltd.
+# originally by John R. Lenton <john.lenton@xxxxxxxxxxxxx>
+# incorporating schemaconfig as configglue.pyschema
+# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@xxxxxxxxxxxxx>
+# 
+# Released under the BSD License (see the file LICENSE)
+# 
+# For bug reports, support, and new releases: http://launchpad.net/configglue
+# 
+###############################################################################
+
+import __builtin__
+import sys
+from optparse import OptionParser
+from collections import namedtuple
+
+from configglue.inischema import (
+    parsers,
+    AttributedConfigParser,
+)
+from configglue.pyschema.parser import SchemaConfigParser
+from configglue.pyschema.schema import (
+    BoolConfigOption,
+    ConfigSection,
+    IntConfigOption,
+    LinesConfigOption,
+    Schema,
+    StringConfigOption,
+)
+
+
+__all__ = [
+    'configglue',
+    'ini2schema',
+    'schemaconfigglue',
+]
+
+SchemaGlue = namedtuple("SchemaGlue", "schema_parser option_parser options args")
+IniGlue = namedtuple("IniGlue", " option_parser options args")
+
+
+def ini2schema(fd, p=None):
+    """
+    Turn a fd that refers to a INI-style schema definition into a
+    SchemaConfigParser object
+
+    @param fd: file-like object to read the schema from
+    @param p: a parser to use. If not set, uses AttributedConfigParser
+    """
+    if p is None:
+        p = AttributedConfigParser()
+    p.readfp(fd)
+    p.parse_all()
+
+    parser2option = {'unicode': StringConfigOption,
+                     'int': IntConfigOption,
+                     'bool': BoolConfigOption,
+                     'lines': LinesConfigOption}
+
+    class MySchema(Schema):
+        pass
+
+    for section_name in p.sections():
+        if section_name == '__main__':
+            section = MySchema
+        else:
+            section = ConfigSection()
+            setattr(MySchema, section_name, section)
+        for option_name in p.options(section_name):
+            option = p.get(section_name, option_name)
+
+            parser = option.attrs.pop('parser', 'unicode')
+            parser_args = option.attrs.pop('parser.args', '').split()
+            parser_fun = getattr(parsers, parser, None)
+            if parser_fun is None:
+                parser_fun = getattr(__builtin__, parser, None)
+            if parser_fun is None:
+                parser_fun = lambda x: x
+
+            attrs = {}
+            option_help = option.attrs.pop('help', None)
+            if option_help is not None:
+                attrs['help'] = option_help
+            if not option.is_empty:
+                attrs['default'] = parser_fun(option.value, *parser_args)
+            option_action = option.attrs.pop('action', None)
+            if option_action is not None:
+                attrs['action'] = option_action
+
+            klass = parser2option.get(parser, StringConfigOption)
+            if parser == 'lines':
+                instance = klass(StringConfigOption(), **attrs)
+            else:
+                instance = klass(**attrs)
+            setattr(section, option_name, instance)
+
+    return SchemaConfigParser(MySchema())
+
+
+def schemaconfigglue(parser, op=None, argv=None):
+    """Populate an OptionParser with options and defaults taken from a
+    fully loaded SchemaConfigParser.
+    """
+
+    def long_name(option):
+        if option.section.name == '__main__':
+            return option.name
+        return option.section.name + '_' + option.name
+
+    def opt_name(option):
+        return long_name(option).replace('-', '_')
+
+    if op is None:
+        op = OptionParser()
+    if argv is None:
+        argv = sys.argv[1:]
+    schema = parser.schema
+
+    for section in schema.sections():
+        if section.name == '__main__':
+            og = op
+        else:
+            og = op.add_option_group(section.name)
+        for option in section.options():
+            kwargs = {}
+            if option.help:
+                kwargs['help'] = option.help
+            kwargs['default'] = parser.get(section.name, option.name)
+            kwargs['action'] = option.action
+            og.add_option('--' + long_name(option), **kwargs)
+    options, args = op.parse_args(argv)
+
+    for section in schema.sections():
+        for option in section.options():
+            value = getattr(options, opt_name(option))
+            if parser.get(section.name, option.name) != value:
+                # the value has been overridden by an argument;
+                # update it.
+                parser.set(section.name, option.name, value)
+
+    return IniGlue(op, options, args)
+
+
+def configglue(schema_class, configs, usage=None):
+    scp = SchemaConfigParser(schema_class())
+    scp.read(configs)
+    if usage is not None:
+        op = OptionParser(usage=usage)
+    else:
+        op = None
+    parser, opts, args = schemaconfigglue(scp, op=op)
+    is_valid, reasons = scp.is_valid(report=True)
+    if not is_valid:
+        parser.error(reasons[0])
+    return SchemaGlue(scp, parser, opts, args)
+

=== modified file 'tests/pyschema/test_glue2glue.py'
--- tests/pyschema/test_glue2glue.py	2010-08-04 12:42:29 +0000
+++ tests/pyschema/test_glue2glue.py	2010-12-19 19:01:37 +0000
@@ -21,7 +21,10 @@
 from StringIO import StringIO
 
 from configglue.inischema import configglue
-from configglue.pyschema import schemaconfigglue, ini2schema
+from configglue.utils import (
+    ini2schema,
+    schemaconfigglue,
+)
 
 
 class TestGlueConvertor(unittest.TestCase):

=== removed file 'tests/pyschema/test_options.py'
--- tests/pyschema/test_options.py	2010-08-04 12:42:29 +0000
+++ tests/pyschema/test_options.py	1970-01-01 00:00:00 +0000
@@ -1,511 +0,0 @@
-###############################################################################
-# 
-# configglue -- glue for your apps' configuration
-# 
-# A library for simple, DRY configuration of applications
-# 
-# (C) 2009--2010 by Canonical Ltd.
-# originally by John R. Lenton <john.lenton@xxxxxxxxxxxxx>
-# incorporating schemaconfig as configglue.pyschema
-# schemaconfig originally by Ricardo Kirkner <ricardo.kirkner@xxxxxxxxxxxxx>
-# 
-# Released under the BSD License (see the file LICENSE)
-# 
-# For bug reports, support, and new releases: http://launchpad.net/configglue
-# 
-###############################################################################
-
-import unittest
-from StringIO import StringIO
-
-from configglue.pyschema.options import (BoolConfigOption, IntConfigOption,
-    StringConfigOption, LinesConfigOption, TupleConfigOption, DictConfigOption)
-from configglue.pyschema.parser import SchemaConfigParser
-from configglue.pyschema.schema import Schema
-
-
-class TestStringConfigOption(unittest.TestCase):
-    def test_init_no_args(self):
-        opt = StringConfigOption()
-        self.assertFalse(opt.null)
-
-    def test_init_null(self):
-        opt = StringConfigOption(null=True)
-        self.assertTrue(opt.null)
-
-    def test_parse_string(self):
-        class MySchema(Schema):
-            foo = StringConfigOption(null=True)
-        config = StringIO("[__main__]\nfoo = 42")
-        expected_values = {'__main__': {'foo': '42'}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO("[__main__]\nfoo = ")
-        expected_values = {'__main__': {'foo': ''}}
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO("[__main__]\nfoo = None")
-        expected_values = {'__main__': {'foo': None}}
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        class MySchema(Schema):
-            foo = StringConfigOption()
-        config = StringIO("[__main__]\nfoo = None")
-        expected_values = {'__main__': {'foo': 'None'}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_default(self):
-        opt = StringConfigOption()
-        self.assertEqual(opt.default, '')
-
-    def test_default_null(self):
-        opt = StringConfigOption(null=True)
-        self.assertEqual(opt.default, None)
-
-
-class TestIntConfigOption(unittest.TestCase):
-    def test_parse_int(self):
-        class MySchema(Schema):
-            foo = IntConfigOption()
-        config = StringIO("[__main__]\nfoo = 42")
-        expected_values = {'__main__': {'foo': 42}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO("[__main__]\nfoo =")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-        config = StringIO("[__main__]\nfoo = bla")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-    def test_default(self):
-        opt = IntConfigOption()
-        self.assertEqual(opt.default, 0)
-
-
-class TestBoolConfigOption(unittest.TestCase):
-    def test_parse_bool(self):
-        class MySchema(Schema):
-            foo = BoolConfigOption()
-        config = StringIO("[__main__]\nfoo = Yes")
-        expected_values = {'__main__': {'foo': True}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO("[__main__]\nfoo = tRuE")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO("[__main__]\nfoo =")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-        config = StringIO("[__main__]\nfoo = bla")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-    def test_default(self):
-        opt = BoolConfigOption()
-        self.assertEqual(opt.default, False)
-
-
-class TestLinesConfigOption(unittest.TestCase):
-    def test_parse_int_lines(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=IntConfigOption())
-
-        config = StringIO("[__main__]\nfoo = 42\n 43\n 44")
-        expected_values = {'__main__': {'foo': [42, 43, 44]}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_bool_lines(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=BoolConfigOption())
-        schema = MySchema()
-        config = StringIO("[__main__]\nfoo = tRuE\n No\n 0\n 1")
-        expected_values = {'__main__': {'foo': [True, False, False, True]}}
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(expected_values, parser.values())
-
-    def test_parse_bool_empty_lines(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=BoolConfigOption())
-        schema = MySchema()
-        config = StringIO("[__main__]\nfoo =")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        expected_values = {'__main__': {'foo': []}}
-        self.assertEqual(expected_values, parser.values())
-
-    def test_parse_bool_invalid_lines(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=BoolConfigOption())
-        schema = MySchema()
-        config = StringIO("[__main__]\nfoo = bla")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-        config = StringIO("[__main__]\nfoo = True\n bla")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-    def test_default(self):
-        opt = LinesConfigOption(item=IntConfigOption())
-        self.assertEqual(opt.default, [])
-
-    def test_remove_duplicates(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=StringConfigOption(),
-                                    remove_duplicates=True)
-        schema = MySchema()
-        config = StringIO("[__main__]\nfoo = bla\n blah\n bla")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEquals({'__main__': {'foo': ['bla', 'blah']}},
-                          parser.values())
-
-    def test_remove_dict_duplicates(self):
-        class MyOtherSchema(Schema):
-            foo = LinesConfigOption(item=DictConfigOption(),
-                                    remove_duplicates=True)
-        schema = MyOtherSchema()
-        config = StringIO("[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz")
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEquals({'__main__': {'foo': [{'bar': 'baz'}]}},
-                          parser.values())
-
-class TestTupleConfigOption(unittest.TestCase):
-    def test_init(self):
-        opt = TupleConfigOption(2)
-        self.assertEqual(opt.length, 2)
-
-    def test_init_no_length(self):
-        opt = TupleConfigOption()
-        self.assertEqual(opt.length, 0)
-        self.assertEqual(opt.default, ())
-
-    def test_parse_no_length(self):
-        class MySchema(Schema):
-            foo = TupleConfigOption()
-
-        config = StringIO('[__main__]\nfoo=1,2,3,4')
-        expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_tuple(self):
-        class MySchema(Schema):
-            foo = TupleConfigOption(length=4)
-        config = StringIO('[__main__]\nfoo = 1, 2, 3, 4')
-        expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-        config = StringIO('[__main__]\nfoo = 1, 2, 3')
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-        config = StringIO('[__main__]\nfoo = ')
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.values)
-
-    def test_default(self):
-        opt = TupleConfigOption(2)
-        self.assertEqual(opt.default, ())
-
-
-class TestDictConfigOption(unittest.TestCase):
-    def test_init(self):
-        opt = DictConfigOption()
-        self.assertEqual(opt.spec, {})
-        self.assertEqual(opt.strict, False)
-
-        spec = {'a': IntConfigOption(), 'b': BoolConfigOption()}
-        opt = DictConfigOption(spec)
-        self.assertEqual(opt.spec, spec)
-        self.assertEqual(opt.strict, False)
-
-        opt = DictConfigOption(spec, strict=True)
-        self.assertEqual(opt.spec, spec)
-        self.assertEqual(opt.strict, True)
-
-    def test_get_extra_sections(self):
-        class MySchema(Schema):
-            foo = DictConfigOption(item=DictConfigOption())
-
-        config = StringIO("""
-[__main__]
-foo=dict1
-[dict1]
-bar=dict2
-[dict2]
-baz=42
-""")
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        expected = ['dict2']
-
-        opt = DictConfigOption(item=DictConfigOption())
-        extra = opt.get_extra_sections('dict1', parser)
-        self.assertEqual(extra, expected)
-
-    def test_parse_dict(self):
-        class MySchema(Schema):
-            foo = DictConfigOption({'bar': StringConfigOption(),
-                                    'baz': IntConfigOption(),
-                                    'bla': BoolConfigOption(),
-                                    })
-        config = StringIO("""[__main__]
-foo = mydict
-[mydict]
-bar=baz
-baz=42
-bla=Yes
-""")
-        expected_values = {
-            '__main__': {
-                'foo': {'bar': 'baz', 'baz': 42, 'bla': True}
-            }
-        }
-
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_raw(self):
-        class MySchema(Schema):
-            foo = DictConfigOption({'bar': StringConfigOption(),
-                                    'baz': IntConfigOption(),
-                                    'bla': BoolConfigOption(),
-                                    })
-        config = StringIO("""[__main__]
-foo = mydict
-[mydict]
-baz=42
-""")
-        expected = {'bar': '', 'baz': '42', 'bla': 'False'}
-
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        parsed = schema.foo.parse('mydict', parser, True)
-        self.assertEqual(parsed, expected)
-
-    def test_parse_invalid_key_in_parsed(self):
-        class MySchema(Schema):
-            foo = DictConfigOption({'bar': IntConfigOption()})
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbaz=2")
-        expected_values = {'__main__': {'foo': {'bar': 0, 'baz': '2'}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_invalid_key_in_spec(self):
-        class MySchema(Schema):
-            foo = DictConfigOption({'bar': IntConfigOption(),
-                                    'baz': IntConfigOption(fatal=True)})
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.parse_all)
-
-    def test_default(self):
-        opt = DictConfigOption({})
-        self.assertEqual(opt.default, {})
-
-    def test_parse_no_strict_missing_args(self):
-        class MySchema(Schema):
-            foo = DictConfigOption({'bar': IntConfigOption()})
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]")
-        expected_values = {'__main__': {'foo': {'bar': 0}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_no_strict_extra_args(self):
-        class MySchema(Schema):
-            foo = DictConfigOption()
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
-        expected_values = {'__main__': {'foo': {'bar': '2'}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_no_strict_with_item(self):
-        class MySchema(Schema):
-            foo = DictConfigOption(
-                      item=DictConfigOption(
-                          item=IntConfigOption()))
-        config = StringIO("""
-[__main__]
-foo = mydict
-[mydict]
-bar = baz
-[baz]
-wham=42
-""")
-        expected_values = {'__main__': {'foo': {'bar': {'wham': 42}}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_strict(self):
-        class MySchema(Schema):
-            spec = {'bar': IntConfigOption()}
-            foo = DictConfigOption(spec, strict=True)
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
-        expected_values = {'__main__': {'foo': {'bar': 2}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_strict_missing_vars(self):
-        class MySchema(Schema):
-            spec = {'bar': IntConfigOption(),
-                    'baz': IntConfigOption()}
-            foo = DictConfigOption(spec, strict=True)
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
-        expected_values = {'__main__': {'foo': {'bar': 2, 'baz': 0}}}
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-    def test_parse_strict_extra_vars(self):
-        class MySchema(Schema):
-            spec = {'bar': IntConfigOption()}
-            foo = DictConfigOption(spec, strict=True)
-
-        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3")
-        parser = SchemaConfigParser(MySchema())
-        parser.readfp(config)
-        self.assertRaises(ValueError, parser.parse_all)
-
-
-class TestLinesOfDictConfigOption(unittest.TestCase):
-    def test_parse_lines_of_dict(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(
-                        DictConfigOption({'bar': StringConfigOption(),
-                                          'baz': IntConfigOption(),
-                                          'bla': BoolConfigOption(),
-                                          }))
-        config = StringIO("""[__main__]
-foo = mylist0
-      mylist1
-[mylist0]
-bar=baz
-baz=42
-bla=Yes
-[mylist1]
-bar=zort
-baz=123
-bla=0
-""")
-        expected_values = {
-            '__main__': {'foo': [{'bar': 'baz', 'baz': 42, 'bla': True},
-                                {'bar': 'zort', 'baz': 123, 'bla': False},
-                               ]}}
-
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-
-class TestDictWithDicts(unittest.TestCase):
-    def test_parse_dict_with_dicts(self):
-        innerspec = {'bar': StringConfigOption(),
-                     'baz': IntConfigOption(),
-                     'bla': BoolConfigOption(),
-                    }
-        spec = {'name': StringConfigOption(),
-                'size': IntConfigOption(),
-                'options': DictConfigOption(innerspec)}
-        class MySchema(Schema):
-            foo = DictConfigOption(spec)
-        config = StringIO("""[__main__]
-foo = outerdict
-[outerdict]
-options = innerdict
-[innerdict]
-bar = something
-baz = 42
-""")
-        expected_values = {
-            '__main__': {'foo': {'name': '', 'size': 0,
-                                'options': {'bar': 'something', 'baz': 42,
-                                            'bla': False}}}}
-        schema = MySchema()
-        parser = SchemaConfigParser(schema)
-        parser.readfp(config)
-        self.assertEqual(parser.values(), expected_values)
-
-
-class TestListOfTuples(unittest.TestCase):
-    def setUp(self):
-        class MySchema(Schema):
-            foo = LinesConfigOption(item=TupleConfigOption(length=3))
-        schema = MySchema()
-        self.parser = SchemaConfigParser(schema)
-
-    def test_parse_list_of_tuples(self):
-        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e, f')
-        expected_values = {
-            '__main__': {'foo': [('a', 'b', 'c'), ('d', 'e', 'f')]}}
-        self.parser.readfp(config)
-        self.assertEqual(self.parser.values(), expected_values)
-
-    def test_parse_wrong_tuple_size(self):
-        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e')
-        self.parser.readfp(config)
-        self.assertRaises(ValueError, self.parser.values)
-
-    def test_parse_empty_tuple(self):
-        config = StringIO('[__main__]\nfoo=()')
-        expected_values = {'__main__': {'foo': [()]}}
-        self.parser.readfp(config)
-        self.assertEqual(self.parser.values(), expected_values)
-
-
-if __name__ == '__main__':
-    unittest.main()

=== modified file 'tests/pyschema/test_parser.py'
--- tests/pyschema/test_parser.py	2010-08-04 12:42:29 +0000
+++ tests/pyschema/test_parser.py	2010-12-19 19:01:37 +0000
@@ -25,12 +25,22 @@
 from ConfigParser import (InterpolationMissingOptionError,
     InterpolationDepthError, NoSectionError)
 
-from configglue.pyschema import ConfigSection
-from configglue.pyschema.options import (BoolConfigOption, DictConfigOption,
-    IntConfigOption, StringConfigOption, LinesConfigOption, TupleConfigOption)
-from configglue.pyschema.schema import Schema
-from configglue.pyschema.parser import (NoOptionError, SchemaConfigParser,
-    SchemaValidationError, CONFIG_FILE_ENCODING)
+from configglue.pyschema.parser import (
+    CONFIG_FILE_ENCODING,
+    NoOptionError,
+    SchemaConfigParser,
+    SchemaValidationError,
+)
+from configglue.pyschema.schema import (
+    BoolConfigOption,
+    ConfigSection,
+    DictConfigOption,
+    IntConfigOption,
+    LinesConfigOption,
+    Schema,
+    StringConfigOption,
+    TupleConfigOption,
+)
 
 
 class TestIncludes(unittest.TestCase):

=== modified file 'tests/pyschema/test_schema.py'
--- tests/pyschema/test_schema.py	2010-08-06 19:47:09 +0000
+++ tests/pyschema/test_schema.py	2010-12-19 19:01:37 +0000
@@ -16,11 +16,19 @@
 ###############################################################################
 
 import unittest
+from StringIO import StringIO
 
-from configglue.pyschema import ConfigSection
-from configglue.pyschema.options import (BoolConfigOption,
-    IntConfigOption, LinesConfigOption)
-from configglue.pyschema.schema import Schema
+from configglue.pyschema.parser import SchemaConfigParser
+from configglue.pyschema.schema import (
+    BoolConfigOption,
+    ConfigSection,
+    DictConfigOption,
+    IntConfigOption,
+    LinesConfigOption,
+    Schema,
+    StringConfigOption,
+    TupleConfigOption,
+)
 
 
 class TestSchema(unittest.TestCase):
@@ -146,3 +154,485 @@
         self.assertFalse(hasattr(self.other.foo, 'baz'))
 
 
+class TestStringConfigOption(unittest.TestCase):
+    def test_init_no_args(self):
+        opt = StringConfigOption()
+        self.assertFalse(opt.null)
+
+    def test_init_null(self):
+        opt = StringConfigOption(null=True)
+        self.assertTrue(opt.null)
+
+    def test_parse_string(self):
+        class MySchema(Schema):
+            foo = StringConfigOption(null=True)
+        config = StringIO("[__main__]\nfoo = 42")
+        expected_values = {'__main__': {'foo': '42'}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO("[__main__]\nfoo = ")
+        expected_values = {'__main__': {'foo': ''}}
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO("[__main__]\nfoo = None")
+        expected_values = {'__main__': {'foo': None}}
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        class MySchema(Schema):
+            foo = StringConfigOption()
+        config = StringIO("[__main__]\nfoo = None")
+        expected_values = {'__main__': {'foo': 'None'}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_default(self):
+        opt = StringConfigOption()
+        self.assertEqual(opt.default, '')
+
+    def test_default_null(self):
+        opt = StringConfigOption(null=True)
+        self.assertEqual(opt.default, None)
+
+
+class TestIntConfigOption(unittest.TestCase):
+    def test_parse_int(self):
+        class MySchema(Schema):
+            foo = IntConfigOption()
+        config = StringIO("[__main__]\nfoo = 42")
+        expected_values = {'__main__': {'foo': 42}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO("[__main__]\nfoo =")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+        config = StringIO("[__main__]\nfoo = bla")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+    def test_default(self):
+        opt = IntConfigOption()
+        self.assertEqual(opt.default, 0)
+
+
+class TestBoolConfigOption(unittest.TestCase):
+    def test_parse_bool(self):
+        class MySchema(Schema):
+            foo = BoolConfigOption()
+        config = StringIO("[__main__]\nfoo = Yes")
+        expected_values = {'__main__': {'foo': True}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO("[__main__]\nfoo = tRuE")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO("[__main__]\nfoo =")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+        config = StringIO("[__main__]\nfoo = bla")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+    def test_default(self):
+        opt = BoolConfigOption()
+        self.assertEqual(opt.default, False)
+
+
+class TestLinesConfigOption(unittest.TestCase):
+    def test_parse_int_lines(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=IntConfigOption())
+
+        config = StringIO("[__main__]\nfoo = 42\n 43\n 44")
+        expected_values = {'__main__': {'foo': [42, 43, 44]}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_bool_lines(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=BoolConfigOption())
+        schema = MySchema()
+        config = StringIO("[__main__]\nfoo = tRuE\n No\n 0\n 1")
+        expected_values = {'__main__': {'foo': [True, False, False, True]}}
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(expected_values, parser.values())
+
+    def test_parse_bool_empty_lines(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=BoolConfigOption())
+        schema = MySchema()
+        config = StringIO("[__main__]\nfoo =")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        expected_values = {'__main__': {'foo': []}}
+        self.assertEqual(expected_values, parser.values())
+
+    def test_parse_bool_invalid_lines(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=BoolConfigOption())
+        schema = MySchema()
+        config = StringIO("[__main__]\nfoo = bla")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+        config = StringIO("[__main__]\nfoo = True\n bla")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+    def test_default(self):
+        opt = LinesConfigOption(item=IntConfigOption())
+        self.assertEqual(opt.default, [])
+
+    def test_remove_duplicates(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=StringConfigOption(),
+                                    remove_duplicates=True)
+        schema = MySchema()
+        config = StringIO("[__main__]\nfoo = bla\n blah\n bla")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEquals({'__main__': {'foo': ['bla', 'blah']}},
+                          parser.values())
+
+    def test_remove_dict_duplicates(self):
+        class MyOtherSchema(Schema):
+            foo = LinesConfigOption(item=DictConfigOption(),
+                                    remove_duplicates=True)
+        schema = MyOtherSchema()
+        config = StringIO("[__main__]\nfoo = bla\n bla\n[bla]\nbar = baz")
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEquals({'__main__': {'foo': [{'bar': 'baz'}]}},
+                          parser.values())
+
+class TestTupleConfigOption(unittest.TestCase):
+    def test_init(self):
+        opt = TupleConfigOption(2)
+        self.assertEqual(opt.length, 2)
+
+    def test_init_no_length(self):
+        opt = TupleConfigOption()
+        self.assertEqual(opt.length, 0)
+        self.assertEqual(opt.default, ())
+
+    def test_parse_no_length(self):
+        class MySchema(Schema):
+            foo = TupleConfigOption()
+
+        config = StringIO('[__main__]\nfoo=1,2,3,4')
+        expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_tuple(self):
+        class MySchema(Schema):
+            foo = TupleConfigOption(length=4)
+        config = StringIO('[__main__]\nfoo = 1, 2, 3, 4')
+        expected_values = {'__main__': {'foo': ('1', '2', '3', '4')}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+        config = StringIO('[__main__]\nfoo = 1, 2, 3')
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+        config = StringIO('[__main__]\nfoo = ')
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.values)
+
+    def test_default(self):
+        opt = TupleConfigOption(2)
+        self.assertEqual(opt.default, ())
+
+
+class TestDictConfigOption(unittest.TestCase):
+    def test_init(self):
+        opt = DictConfigOption()
+        self.assertEqual(opt.spec, {})
+        self.assertEqual(opt.strict, False)
+
+        spec = {'a': IntConfigOption(), 'b': BoolConfigOption()}
+        opt = DictConfigOption(spec)
+        self.assertEqual(opt.spec, spec)
+        self.assertEqual(opt.strict, False)
+
+        opt = DictConfigOption(spec, strict=True)
+        self.assertEqual(opt.spec, spec)
+        self.assertEqual(opt.strict, True)
+
+    def test_get_extra_sections(self):
+        class MySchema(Schema):
+            foo = DictConfigOption(item=DictConfigOption())
+
+        config = StringIO("""
+[__main__]
+foo=dict1
+[dict1]
+bar=dict2
+[dict2]
+baz=42
+""")
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        expected = ['dict2']
+
+        opt = DictConfigOption(item=DictConfigOption())
+        extra = opt.get_extra_sections('dict1', parser)
+        self.assertEqual(extra, expected)
+
+    def test_parse_dict(self):
+        class MySchema(Schema):
+            foo = DictConfigOption({'bar': StringConfigOption(),
+                                    'baz': IntConfigOption(),
+                                    'bla': BoolConfigOption(),
+                                    })
+        config = StringIO("""[__main__]
+foo = mydict
+[mydict]
+bar=baz
+baz=42
+bla=Yes
+""")
+        expected_values = {
+            '__main__': {
+                'foo': {'bar': 'baz', 'baz': 42, 'bla': True}
+            }
+        }
+
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_raw(self):
+        class MySchema(Schema):
+            foo = DictConfigOption({'bar': StringConfigOption(),
+                                    'baz': IntConfigOption(),
+                                    'bla': BoolConfigOption(),
+                                    })
+        config = StringIO("""[__main__]
+foo = mydict
+[mydict]
+baz=42
+""")
+        expected = {'bar': '', 'baz': '42', 'bla': 'False'}
+
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        parsed = schema.foo.parse('mydict', parser, True)
+        self.assertEqual(parsed, expected)
+
+    def test_parse_invalid_key_in_parsed(self):
+        class MySchema(Schema):
+            foo = DictConfigOption({'bar': IntConfigOption()})
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbaz=2")
+        expected_values = {'__main__': {'foo': {'bar': 0, 'baz': '2'}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_invalid_key_in_spec(self):
+        class MySchema(Schema):
+            foo = DictConfigOption({'bar': IntConfigOption(),
+                                    'baz': IntConfigOption(fatal=True)})
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.parse_all)
+
+    def test_default(self):
+        opt = DictConfigOption({})
+        self.assertEqual(opt.default, {})
+
+    def test_parse_no_strict_missing_args(self):
+        class MySchema(Schema):
+            foo = DictConfigOption({'bar': IntConfigOption()})
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]")
+        expected_values = {'__main__': {'foo': {'bar': 0}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_no_strict_extra_args(self):
+        class MySchema(Schema):
+            foo = DictConfigOption()
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        expected_values = {'__main__': {'foo': {'bar': '2'}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_no_strict_with_item(self):
+        class MySchema(Schema):
+            foo = DictConfigOption(
+                      item=DictConfigOption(
+                          item=IntConfigOption()))
+        config = StringIO("""
+[__main__]
+foo = mydict
+[mydict]
+bar = baz
+[baz]
+wham=42
+""")
+        expected_values = {'__main__': {'foo': {'bar': {'wham': 42}}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_strict(self):
+        class MySchema(Schema):
+            spec = {'bar': IntConfigOption()}
+            foo = DictConfigOption(spec, strict=True)
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        expected_values = {'__main__': {'foo': {'bar': 2}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_strict_missing_vars(self):
+        class MySchema(Schema):
+            spec = {'bar': IntConfigOption(),
+                    'baz': IntConfigOption()}
+            foo = DictConfigOption(spec, strict=True)
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2")
+        expected_values = {'__main__': {'foo': {'bar': 2, 'baz': 0}}}
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+    def test_parse_strict_extra_vars(self):
+        class MySchema(Schema):
+            spec = {'bar': IntConfigOption()}
+            foo = DictConfigOption(spec, strict=True)
+
+        config = StringIO("[__main__]\nfoo=mydict\n[mydict]\nbar=2\nbaz=3")
+        parser = SchemaConfigParser(MySchema())
+        parser.readfp(config)
+        self.assertRaises(ValueError, parser.parse_all)
+
+
+class TestLinesOfDictConfigOption(unittest.TestCase):
+    def test_parse_lines_of_dict(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(
+                        DictConfigOption({'bar': StringConfigOption(),
+                                          'baz': IntConfigOption(),
+                                          'bla': BoolConfigOption(),
+                                          }))
+        config = StringIO("""[__main__]
+foo = mylist0
+      mylist1
+[mylist0]
+bar=baz
+baz=42
+bla=Yes
+[mylist1]
+bar=zort
+baz=123
+bla=0
+""")
+        expected_values = {
+            '__main__': {'foo': [{'bar': 'baz', 'baz': 42, 'bla': True},
+                                {'bar': 'zort', 'baz': 123, 'bla': False},
+                               ]}}
+
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+
+class TestDictWithDicts(unittest.TestCase):
+    def test_parse_dict_with_dicts(self):
+        innerspec = {'bar': StringConfigOption(),
+                     'baz': IntConfigOption(),
+                     'bla': BoolConfigOption(),
+                    }
+        spec = {'name': StringConfigOption(),
+                'size': IntConfigOption(),
+                'options': DictConfigOption(innerspec)}
+        class MySchema(Schema):
+            foo = DictConfigOption(spec)
+        config = StringIO("""[__main__]
+foo = outerdict
+[outerdict]
+options = innerdict
+[innerdict]
+bar = something
+baz = 42
+""")
+        expected_values = {
+            '__main__': {'foo': {'name': '', 'size': 0,
+                                'options': {'bar': 'something', 'baz': 42,
+                                            'bla': False}}}}
+        schema = MySchema()
+        parser = SchemaConfigParser(schema)
+        parser.readfp(config)
+        self.assertEqual(parser.values(), expected_values)
+
+
+class TestListOfTuples(unittest.TestCase):
+    def setUp(self):
+        class MySchema(Schema):
+            foo = LinesConfigOption(item=TupleConfigOption(length=3))
+        schema = MySchema()
+        self.parser = SchemaConfigParser(schema)
+
+    def test_parse_list_of_tuples(self):
+        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e, f')
+        expected_values = {
+            '__main__': {'foo': [('a', 'b', 'c'), ('d', 'e', 'f')]}}
+        self.parser.readfp(config)
+        self.assertEqual(self.parser.values(), expected_values)
+
+    def test_parse_wrong_tuple_size(self):
+        config = StringIO('[__main__]\nfoo = a, b, c\n      d, e')
+        self.parser.readfp(config)
+        self.assertRaises(ValueError, self.parser.values)
+
+    def test_parse_empty_tuple(self):
+        config = StringIO('[__main__]\nfoo=()')
+        expected_values = {'__main__': {'foo': [()]}}
+        self.parser.readfp(config)
+        self.assertEqual(self.parser.values(), expected_values)
+

=== modified file 'tests/pyschema/test_schemaconfig.py'
--- tests/pyschema/test_schemaconfig.py	2010-12-18 21:12:24 +0000
+++ tests/pyschema/test_schemaconfig.py	2010-12-19 19:01:37 +0000
@@ -21,10 +21,17 @@
 
 from mock import patch, Mock
 
-from configglue.pyschema import ConfigOption, ConfigSection, schemaconfigglue, configglue
-from configglue.pyschema.options import IntConfigOption
+from configglue.utils import (
+    configglue,
+    schemaconfigglue,
+)
 from configglue.pyschema.parser import SchemaConfigParser
-from configglue.pyschema.schema import Schema
+from configglue.pyschema.schema import (
+    ConfigOption,
+    ConfigSection,
+    IntConfigOption,
+    Schema,
+)
 
 
 class TestConfigOption(unittest.TestCase):
@@ -183,8 +190,8 @@
 
 
 class ConfigglueTestCase(unittest.TestCase):
-    @patch('configglue.pyschema.SchemaConfigParser')
-    @patch('configglue.pyschema.schemaconfigglue')
+    @patch('configglue.pyschema.parser.SchemaConfigParser')
+    @patch('configglue.utils.schemaconfigglue')
     def test_configglue(self, mock_schemaconfigglue, mock_schema_parser):
         # prepare mocks
         expected_schema_parser = Mock()
@@ -216,8 +223,8 @@
         self.assertEqual(glue.options, expected_options)
         self.assertEqual(glue.args, expected_args)
 
-    @patch('configglue.pyschema.SchemaConfigParser')
-    @patch('configglue.pyschema.schemaconfigglue')
+    @patch('configglue.utils.SchemaConfigParser')
+    @patch('configglue.utils.schemaconfigglue')
     def test_configglue(self, mock_schemaconfigglue, mock_schema_parser):
         # prepare mocks
         expected_schema_parser = Mock()
@@ -250,9 +257,9 @@
         self.assertEqual(glue.options, expected_options)
         self.assertEqual(glue.args, expected_args)
 
-    @patch('configglue.pyschema.OptionParser')
-    @patch('configglue.pyschema.SchemaConfigParser')
-    @patch('configglue.pyschema.schemaconfigglue')
+    @patch('configglue.utils.OptionParser')
+    @patch('configglue.utils.SchemaConfigParser')
+    @patch('configglue.utils.schemaconfigglue')
     def test_configglue_with_usage(self, mock_schemaconfigglue,
         mock_schema_parser, mock_option_parser):
         # prepare mocks


Follow ups