configglue team mailing list archive
-
configglue team
-
Mailing list archive
-
Message #00033
[Merge] lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue
Ricardo Kirkner has proposed merging lp:~ricardokirkner/configglue/remove-circular-dependencies into lp:configglue with lp:~ricardokirkner/configglue/simplified as a prerequisite.
Requested reviews:
Configglue developers (configglue)
This branch refactors some imports to remove circular dependencies, and cleaned up a bit the project structure.
--
https://code.launchpad.net/~ricardokirkner/configglue/remove-circular-dependencies/+merge/44151
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-18 22:36:44 +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 22:36:44 +0000
+++ configglue/pyschema/__init__.py 2010-12-18 22:36:44 +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-18 22:36:44 +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-18 22:36:44 +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-18 22:36:44 +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-18 22:36:44 +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-18 22:36:44 +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-18 22:36:44 +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 22:36:44 +0000
+++ tests/pyschema/test_schemaconfig.py 2010-12-18 22:36:44 +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