vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / utils / formatting.py
diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/utils/formatting.py b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/formatting.py
new file mode 100644 (file)
index 0000000..fa34b7d
--- /dev/null
@@ -0,0 +1,235 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+String formatting and string-based format utilities.
+"""
+
+import json
+from types import MethodType
+
+from ruamel import yaml  # @UnresolvedImport
+
+from .collections import FrozenList, FrozenDict, StrictList, StrictDict, OrderedDict
+
+
+PLURALIZE_EXCEPTIONS = {}
+
+
+# Add our types to ruamel.yaml (for round trips)
+yaml.representer.RoundTripRepresenter.add_representer(
+    FrozenList, yaml.representer.RoundTripRepresenter.represent_list)
+yaml.representer.RoundTripRepresenter.add_representer(
+    FrozenDict, yaml.representer.RoundTripRepresenter.represent_dict)
+yaml.representer.RoundTripRepresenter.add_representer(
+    StrictList, yaml.representer.RoundTripRepresenter.represent_list)
+yaml.representer.RoundTripRepresenter.add_representer(
+    StrictDict, yaml.representer.RoundTripRepresenter.represent_dict)
+
+# Without this, ruamel.yaml will output "!!omap" types, which is
+# technically correct but unnecessarily verbose for our uses
+yaml.representer.RoundTripRepresenter.add_representer(
+    OrderedDict, yaml.representer.RoundTripRepresenter.represent_dict)
+
+
+class JsonAsRawEncoder(json.JSONEncoder):
+    """
+    A :class:`JSONEncoder` that will use the ``as_raw`` property of objects if available.
+    """
+    def raw_encoder_default(self, obj):
+        try:
+            return iter(obj)
+        except TypeError:
+            if hasattr(obj, 'as_raw'):
+                return as_raw(obj)
+            return str(obj)
+        return super(JsonAsRawEncoder, self).default(obj)
+
+    def __init__(self, *args, **kwargs):
+        kwargs['default'] = self.raw_encoder_default
+        super(JsonAsRawEncoder, self).__init__(*args, **kwargs)
+
+
+class YamlAsRawDumper(yaml.dumper.RoundTripDumper):  # pylint: disable=too-many-ancestors
+    """
+    A :class:`RoundTripDumper` that will use the ``as_raw`` property of objects if available.
+    """
+
+    def represent_data(self, data):
+        if hasattr(data, 'as_raw'):
+            data = as_raw(data)
+        return super(YamlAsRawDumper, self).represent_data(data)
+
+
+def decode_list(data):
+    decoded_list = []
+    for item in data:
+        if isinstance(item, unicode):
+            item = item.encode('utf-8')
+        elif isinstance(item, list):
+            item = decode_list(item)
+        elif isinstance(item, dict):
+            item = decode_dict(item)
+        decoded_list.append(item)
+    return decoded_list
+
+
+def decode_dict(data):
+    decoded_dict = {}
+    for key, value in data.iteritems():
+        if isinstance(key, unicode):
+            key = key.encode('utf-8')
+        if isinstance(value, unicode):
+            value = value.encode('utf-8')
+        elif isinstance(value, list):
+            value = decode_list(value)
+        elif isinstance(value, dict):
+            value = decode_dict(value)
+        decoded_dict[key] = value
+    return decoded_dict
+
+
+def safe_str(value):
+    """
+    Like :class:`str` coercion, but makes sure that Unicode strings are properly encoded, and will
+    never return ``None``.
+    """
+
+    try:
+        return str(value)
+    except UnicodeEncodeError:
+        return unicode(value).encode('utf8')
+
+
+def safe_repr(value):
+    """
+    Like :func:`repr`, but calls :func:`as_raw` and :func:`as_agnostic` first.
+    """
+
+    return repr(as_agnostic(as_raw(value)))
+
+
+def string_list_as_string(strings):
+    """
+    Nice representation of a list of strings.
+    """
+
+    if not strings:
+        return 'none'
+    return ', '.join('"{0}"'.format(safe_str(v)) for v in strings)
+
+
+def pluralize(noun):
+    plural = PLURALIZE_EXCEPTIONS.get(noun)
+    if plural is not None:
+        return plural
+    elif noun.endswith('s'):
+        return '{0}es'.format(noun)
+    elif noun.endswith('y'):
+        return '{0}ies'.format(noun[:-1])
+    else:
+        return '{0}s'.format(noun)
+
+
+def as_raw(value):
+    """
+    Converts values using their ``as_raw`` property, if it exists, recursively.
+    """
+
+    if hasattr(value, 'as_raw'):
+        value = value.as_raw
+        if isinstance(value, MethodType):
+            # Old-style Python classes don't support properties
+            value = value()
+    elif isinstance(value, list):
+        value = list(value)
+        for i, v in enumerate(value):
+            value[i] = as_raw(v)
+    elif isinstance(value, dict):
+        value = dict(value)
+        for k, v in value.iteritems():
+            value[k] = as_raw(v)
+    return value
+
+
+def as_raw_list(value):
+    """
+    Assuming value is a list, converts its values using :func:`as_raw`.
+    """
+
+    if value is None:
+        return []
+    if isinstance(value, dict):
+        value = value.itervalues()
+    return [as_raw(v) for v in value]
+
+
+def as_raw_dict(value):
+    """
+    Assuming value is a dict, converts its values using :func:`as_raw`. The keys are left as is.
+    """
+
+    if value is None:
+        return OrderedDict()
+    return OrderedDict((
+        (k, as_raw(v)) for k, v in value.iteritems()))
+
+
+def as_agnostic(value):
+    """
+    Converts subclasses of list and dict to standard lists and dicts, and Unicode strings to
+    non-Unicode if possible, recursively.
+
+    Useful for creating human-readable output of structures.
+    """
+
+    if isinstance(value, unicode):
+        try:
+            value = str(value)
+        except UnicodeEncodeError:
+            pass
+    elif isinstance(value, list):
+        value = list(value)
+    elif isinstance(value, dict):
+        value = dict(value)
+
+    if isinstance(value, list):
+        for i, _ in enumerate(value):
+            value[i] = as_agnostic(value[i])
+    elif isinstance(value, dict):
+        for k, v in value.iteritems():
+            value[k] = as_agnostic(v)
+
+    return value
+
+
+def json_dumps(value, indent=2):
+    """
+    JSON dumps that supports Unicode and the ``as_raw`` property of objects if available.
+    """
+
+    return json.dumps(value, indent=indent, ensure_ascii=False, cls=JsonAsRawEncoder)
+
+
+def yaml_dumps(value, indent=2):
+    """
+    YAML dumps that supports Unicode and the ``as_raw`` property of objects if available.
+    """
+
+    return yaml.dump(value, indent=indent, allow_unicode=True, Dumper=YamlAsRawDumper)
+
+
+def yaml_loads(value):
+    return yaml.load(value, Loader=yaml.SafeLoader)