diff options
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/utils/formatting.py')
-rw-r--r-- | azure/aria/aria-extension-cloudify/src/aria/aria/utils/formatting.py | 235 |
1 files changed, 235 insertions, 0 deletions
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 index 0000000..fa34b7d --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/utils/formatting.py @@ -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) |