diff configmix/yaml.py @ 5:dc058099a4cb

Renamed the project from "MixConfig" to "ConfigMix"
author Franz Glasner <hg@dom66.de>
date Tue, 08 Mar 2016 20:11:17 +0100
parents mixconfig/yaml.py@bedc4f95b9e9
children ce290b10dac5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configmix/yaml.py	Tue Mar 08 20:11:17 2016 +0100
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+r"""Simple wrapper for yaml to support all-unicode strings when loading
+configuration files.
+
+"""
+
+from __future__ import division, print_function, absolute_import
+
+try:
+    from collections import OrderedDict
+except ImportError:
+    try:
+        from ordereddict import OrderedDict
+    except ImportError:
+        OrderedDict = None
+import yaml
+import yaml.constructor
+
+
+__all__ = ["safe_load", "safe_load_all", "load", "load_all"]
+
+
+class ConfigLoader(yaml.Loader):
+
+    """A YAML loader, which makes all !!str strings to Unicode.  Standard
+    PyYAML does this only in the non-ASCII case.
+
+    If an `OrderedDict` implementation is available then all "map" and
+    "omap" nodes are constructed as `OrderedDict`.
+    This is against YAML specs but within configuration files it seems
+    more natural.
+
+    """
+
+    def construct_yaml_str(self, node):
+        return self.construct_scalar(node)
+
+    if OrderedDict:
+
+        #
+        # From https://pypi.python.org/pypi/yamlordereddictloader/0.1.1
+        # (MIT License)
+        #
+
+        def construct_yaml_map(self, node):
+            data = OrderedDict()
+            yield data
+            value = self.construct_mapping(node)
+            data.update(value)
+
+        def construct_mapping(self, node, deep=False):
+            if isinstance(node, yaml.MappingNode):
+                self.flatten_mapping(node)
+            else:
+                raise yaml.constructor.ConstructorError(None, None,
+                    'expected a mapping node, but found %s' % node.id,
+                    node.start_mark)
+
+            mapping = OrderedDict()
+            for key_node, value_node in node.value:
+                key = self.construct_object(key_node, deep=deep)
+                try:
+                    hash(key)
+                except TypeError as err:
+                    raise yaml.constructor.ConstructorError(
+                        'while constructing a mapping', node.start_mark,
+                        'found unacceptable key (%s)' % err, key_node.start_mark)
+                value = self.construct_object(value_node, deep=deep)
+                mapping[key] = value
+            return mapping
+
+
+ConfigLoader.add_constructor(
+    "tag:yaml.org,2002:str",
+    ConfigLoader.construct_yaml_str)
+if OrderedDict:
+    ConfigLoader.add_constructor(
+        "tag:yaml.org,2002:map",
+        ConfigLoader.construct_yaml_map)
+    ConfigLoader.add_constructor(
+        "tag:yaml.org,2002:omap",
+        ConfigLoader.construct_yaml_map)
+
+
+class ConfigSafeLoader(yaml.SafeLoader):
+
+    """A safe YAML loader, which makes all !!str strings to Unicode.
+    Standard PyYAML does this only in the non-ASCII case.
+
+    If an `OrderedDict` implementation is available then all "map" and
+    "omap" nodes are constructed as `OrderedDict`.
+    This is against YAML specs but within configuration files it seems
+    more natural.
+
+    """
+
+    def construct_yaml_str(self, node):
+        return self.construct_scalar(node)
+
+    if OrderedDict:
+
+        #
+        # From https://pypi.python.org/pypi/yamlordereddictloader/0.1.1
+        # (MIT License)
+        #
+
+        def construct_yaml_map(self, node):
+            data = OrderedDict()
+            yield data
+            value = self.construct_mapping(node)
+            data.update(value)
+
+        def construct_mapping(self, node, deep=False):
+            if isinstance(node, yaml.MappingNode):
+                self.flatten_mapping(node)
+            else:
+                raise yaml.constructor.ConstructorError(None, None,
+                    'expected a mapping node, but found %s' % node.id,
+                    node.start_mark)
+
+            mapping = OrderedDict()
+            for key_node, value_node in node.value:
+                key = self.construct_object(key_node, deep=deep)
+                try:
+                    hash(key)
+                except TypeError as err:
+                    raise yaml.constructor.ConstructorError(
+                        'while constructing a mapping', node.start_mark,
+                        'found unacceptable key (%s)' % err, key_node.start_mark)
+                value = self.construct_object(value_node, deep=deep)
+                mapping[key] = value
+            return mapping
+
+
+ConfigSafeLoader.add_constructor(
+    "tag:yaml.org,2002:str",
+    ConfigSafeLoader.construct_yaml_str)
+if OrderedDict:
+    ConfigSafeLoader.add_constructor(
+        "tag:yaml.org,2002:map",
+        ConfigSafeLoader.construct_yaml_map)
+    ConfigSafeLoader.add_constructor(
+        "tag:yaml.org,2002:omap",
+        ConfigSafeLoader.construct_yaml_map)
+
+
+def load(stream, Loader=ConfigLoader):
+    return yaml.load(stream, Loader)
+
+
+def load_all(stream, Loader=ConfigLoader):
+    return yaml.load_all(stream, Loader)
+
+
+def safe_load(stream):
+    return yaml.load(stream, Loader=ConfigSafeLoader)
+
+
+def safe_load_all(stream):
+    return yaml.load_all(stream, Loader=ConfigSafeLoader)