Antoine Millet avatar Antoine Millet committed 02ba7a1

Added TypedArray container

Comments (0)

Files changed (2)

docs/schema/containers.rst

 ----------------
 
 .. autoclass:: dotconf.schema.containers.Array
+.. autoclass:: dotconf.schema.containers.TypedArray

dotconf/schema/containers.py

     import argparse
 except ImportError:
     argparse = None
+from itertools import izip
 
 from dotconf.tree import ConfigSection, ConfigValue
 from dotconf.schema import Container, ArgparseContainer, ValidationError
         return value
 
 
+class TypedArray(ArgparseContainer):
+
+    """ An array container used to store a fixed size list of scalar values
+        with specified type for each of them.
+
+    :param values_types: types of each item in a list
+    :param default: the default value of the container
+    """
+
+    def __init__(self, values_types, default=required, **kwargs):
+        super(TypedArray, self).__init__(**kwargs)
+        self._types = values_types
+        self._default = default
+
+    def populate_argparse(self, parser, name):
+        value = self
+        class Action(argparse.Action):
+            def __call__(self, parser, namespace, values, option_string=None):
+                value._argparse_value = ConfigValue(name, values)
+        if self._argparse_names:
+            nargs = '*'
+            parser.add_argument(*self._argparse_names, action=Action,
+                                type=str, nargs=nargs,
+                                metavar=self._argparse_metavar,
+                                help=self._argparse_help)
+
+    def validate(self, value):
+        if self._argparse_value is not None:
+            value = self._argparse_value
+        if value is None:
+            if self._default is required:
+                raise ValidationError('this value is required')
+            else:
+                return ConfigValue(None, self._default)
+        else:
+            values = value.value
+            if not isinstance(values, list):
+                values = [values]
+            validated_list = []
+            if len(values) != len(self._types):
+                raise ValidationError('bad array size (should be %d, found %d '
+                                      'items)' % (len(self._types), len(values)))
+
+            for i, (item, item_type) in enumerate(izip(values, self._types)):
+                try:
+                    item = item_type.validate(item)
+                except ValidationError as err:
+                    raise ValidationError('item #%d, %s' % (i, err),
+                                          position=value.position)
+                else:
+                    validated_list.append(item)
+            return ConfigValue(value.name, validated_list, position=value.position)
+
+
 class Section(Container):
 
     """ A section container used to store a mapping between name and other
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.