ansible-later/env_27/lib/python2.7/site-packages/anyconfig/backend/yaml/pyyaml.py
2019-04-11 13:00:36 +02:00

231 lines
7.3 KiB
Python

#
# Copyright (C) 2011 - 2018 Satoru SATOH <ssato @ redhat.com>
# License: MIT
#
# type() is used to exactly match check instead of isinstance here.
# pylint: disable=unidiomatic-typecheck
r"""YAML backend:
- Format to support: YAML, http://yaml.org
- Requirements:
- ruamel.yaml, https://bitbucket.org/ruamel/yaml
- or PyYAML (yaml), http://pyyaml.org
- Development Status :: 5 - Production/Stable
- Limitations:
- Resuls is not ordered even if 'ac_ordered' or 'ac_dict' was given.
- Special options:
- All keyword options of yaml.safe_load, yaml.load, yaml.safe_dump and
yaml.dump should work.
- Use 'ac_safe' boolean keyword option if you prefer to call yaml.safe_load
and yaml.safe_dump instead of yaml.load and yaml.dump. Please note that
this option conflicts with 'ac_dict' option and these options cannot be
used at the same time.
- See also: http://pyyaml.org/wiki/PyYAMLDocumentation
Changelog:
.. versionchanged:: 0.9.6
- Add support of loading primitives other than mapping objects.
.. versionchanged:: 0.9.3
- Try ruamel.yaml instead of yaml (PyYAML) if it's available.
.. versionchanged:: 0.3
- Changed special keyword option 'ac_safe' from 'safe' to avoid
possibility of option conflicts in the future.
"""
from __future__ import absolute_import
import yaml
try:
from yaml import CSafeLoader as Loader, CDumper as Dumper
except ImportError:
from yaml import SafeLoader as Loader, Dumper
import anyconfig.backend.base
import anyconfig.compat
import anyconfig.utils
_MAPPING_TAG = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
def filter_from_options(key, options):
"""
:param key: Key str in options
:param options: Mapping object
:return:
New mapping object from `options` in which the item with `key` filtered
>>> filter_from_options('a', dict(a=1, b=2))
{'b': 2}
"""
return anyconfig.utils.filter_options([k for k in options.keys()
if k != key], options)
def _customized_loader(container, loader=Loader, mapping_tag=_MAPPING_TAG):
"""
Create or update loader with making given callble `container` to make
mapping objects such as dict and OrderedDict, used to construct python
object from yaml mapping node internally.
:param container: Set container used internally
"""
def construct_mapping(loader, node, deep=False):
"""Construct python object from yaml mapping node, based on
:meth:`yaml.BaseConstructor.construct_mapping` in PyYAML (MIT).
"""
loader.flatten_mapping(node)
if not isinstance(node, yaml.MappingNode):
msg = "expected a mapping node, but found %s" % node.id
raise yaml.constructor.ConstructorError(None, None, msg,
node.start_mark)
mapping = container()
for key_node, value_node in node.value:
key = loader.construct_object(key_node, deep=deep)
try:
hash(key)
except TypeError as exc:
eargs = ("while constructing a mapping",
node.start_mark,
"found unacceptable key (%s)" % exc,
key_node.start_mark)
raise yaml.constructor.ConstructorError(*eargs)
value = loader.construct_object(value_node, deep=deep)
mapping[key] = value
return mapping
tag = "tag:yaml.org,2002:python/unicode"
def construct_ustr(loader, node):
"""Unicode string constructor"""
return loader.construct_scalar(node)
try:
loader.add_constructor(tag, construct_ustr)
except NameError:
pass
if type(container) != dict:
loader.add_constructor(mapping_tag, construct_mapping)
return loader
def _customized_dumper(container, dumper=Dumper):
"""
Coutnerpart of :func:`_customized_loader` for dumpers.
"""
def container_representer(dumper, data, mapping_tag=_MAPPING_TAG):
"""Container representer.
"""
return dumper.represent_mapping(mapping_tag, data.items())
def ustr_representer(dumper, data):
"""Unicode string representer"""
tag = "tag:yaml.org,2002:python/unicode"
return dumper.represent_scalar(tag, data)
try:
dumper.add_representer(unicode, ustr_representer)
except NameError:
pass
if type(container) != dict:
dumper.add_representer(container, container_representer)
return dumper
def yml_fnc(fname, *args, **options):
"""An wrapper of yaml.safe_load, yaml.load, yaml.safe_dump and yaml.dump.
:param fname:
"load" or "dump", not checked but it should be OK.
see also :func:`yml_load` and :func:`yml_dump`
:param args: [stream] for load or [cnf, stream] for dump
:param options: keyword args may contain "ac_safe" to load/dump safely
"""
key = "ac_safe"
fnc = getattr(yaml, r"safe_" + fname if options.get(key) else fname)
return fnc(*args, **filter_from_options(key, options))
def yml_load(stream, container, yml_fnc=yml_fnc, **options):
"""An wrapper of yaml.safe_load and yaml.load.
:param stream: a file or file-like object to load YAML content
:param container: callble to make a container object
:return: Mapping object
"""
if options.get("ac_safe", False):
options = {} # yaml.safe_load does not process Loader opts.
elif not options.get("Loader"):
maybe_container = options.get("ac_dict", False)
if maybe_container and callable(maybe_container):
container = maybe_container
options["Loader"] = _customized_loader(container)
ret = yml_fnc("load", stream, **filter_from_options("ac_dict", options))
if ret is None:
return container()
return ret
def yml_dump(data, stream, yml_fnc=yml_fnc, **options):
"""An wrapper of yaml.safe_dump and yaml.dump.
:param data: Some data to dump
:param stream: a file or file-like object to dump YAML data
"""
_is_dict = anyconfig.utils.is_dict_like(data)
if options.get("ac_safe", False):
options = {}
elif not options.get("Dumper", False) and _is_dict:
# TODO: Any other way to get its constructor?
maybe_container = options.get("ac_dict", type(data))
options["Dumper"] = _customized_dumper(maybe_container)
if _is_dict:
# Type information and the order of items are lost on dump currently.
data = anyconfig.dicts.convert_to(data, ac_dict=dict)
options = filter_from_options("ac_dict", options)
return yml_fnc("dump", data, stream, **options)
class Parser(anyconfig.backend.base.StreamParser):
"""
Parser for YAML files.
"""
_type = "yaml"
_cid = "pyyaml"
_extensions = ["yaml", "yml"]
_load_opts = ["Loader", "ac_safe", "ac_dict"]
_dump_opts = ["stream", "ac_safe", "Dumper", "default_style",
"default_flow_style", "canonical", "indent", "width",
"allow_unicode", "line_break", "encoding", "explicit_start",
"explicit_end", "version", "tags"]
_ordered = True
_allow_primitives = True
_dict_opts = ["ac_dict"]
load_from_stream = anyconfig.backend.base.to_method(yml_load)
dump_to_stream = anyconfig.backend.base.to_method(yml_dump)
# vim:sw=4:ts=4:et: