From 4f8286b06ad9c5a6c058396997c096a9f64ccf0d Mon Sep 17 00:00:00 2001 From: Satoru SATOH Date: Sat, 9 Feb 2019 00:14:51 +0900 Subject: [PATCH] enhancement: make ruamel.yaml based backend works w/o PyYAML Make ruamel.yaml based backend works w/o PyYAML by: - Introduce anyconfig.backend.yaml.common to move common parts from anyconfig.backend.yamlpyyaml - Remove dependency to anyconfig.backend.yaml.pyyaml from anyconfig.backend.yaml.ruamel_yaml and simplify it This enhanement also might close the issue #99. --- anyconfig/backend/yaml/common.py | 36 +++++++++++++++++++++++++++ anyconfig/backend/yaml/pyyaml.py | 30 ++++++---------------- anyconfig/backend/yaml/ruamel_yaml.py | 22 +++++++++++++--- 3 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 anyconfig/backend/yaml/common.py diff --git a/anyconfig/backend/yaml/common.py b/anyconfig/backend/yaml/common.py new file mode 100644 index 00000000..a8b8a6ef --- /dev/null +++ b/anyconfig/backend/yaml/common.py @@ -0,0 +1,36 @@ +# +# Copyright (C) 2011 - 2018 Satoru SATOH +# Copyright (C) 2019 Satoru SATOH +# License: MIT +# +r"""Common parts for YAML backends: +""" +from __future__ import absolute_import +import anyconfig.backend.base + + +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) + + +class Parser(anyconfig.backend.base.StreamParser): + """ + Parser for YAML files. + """ + _type = "yaml" + _extensions = ["yaml", "yml"] + _ordered = True + _allow_primitives = True + _dict_opts = ["ac_dict"] + +# vim:sw=4:ts=4:et: diff --git a/anyconfig/backend/yaml/pyyaml.py b/anyconfig/backend/yaml/pyyaml.py index 1d03e31c..f78c9b5c 100644 --- a/anyconfig/backend/yaml/pyyaml.py +++ b/anyconfig/backend/yaml/pyyaml.py @@ -49,22 +49,10 @@ import anyconfig.compat import anyconfig.utils +from . import common -_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) +_MAPPING_TAG = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG def _customized_loader(container, loader=Loader, mapping_tag=_MAPPING_TAG): @@ -151,7 +139,7 @@ def yml_fnc(fname, *args, **options): """ key = "ac_safe" fnc = getattr(yaml, r"safe_" + fname if options.get(key) else fname) - return fnc(*args, **filter_from_options(key, options)) + return fnc(*args, **common.filter_from_options(key, options)) def yml_load(stream, container, yml_fnc=yml_fnc, **options): @@ -171,7 +159,8 @@ def yml_load(stream, container, yml_fnc=yml_fnc, **options): options["Loader"] = _customized_loader(container) - ret = yml_fnc("load", stream, **filter_from_options("ac_dict", options)) + ret = yml_fnc("load", stream, + **common.filter_from_options("ac_dict", options)) if ret is None: return container() @@ -196,26 +185,21 @@ def yml_dump(data, stream, yml_fnc=yml_fnc, **options): 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) + options = common.filter_from_options("ac_dict", options) return yml_fnc("dump", data, stream, **options) -class Parser(anyconfig.backend.base.StreamParser): +class Parser(common.Parser): """ 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) diff --git a/anyconfig/backend/yaml/ruamel_yaml.py b/anyconfig/backend/yaml/ruamel_yaml.py index ffccd801..c91f12b8 100644 --- a/anyconfig/backend/yaml/ruamel_yaml.py +++ b/anyconfig/backend/yaml/ruamel_yaml.py @@ -39,9 +39,10 @@ from __future__ import absolute_import import ruamel.yaml as ryaml +import anyconfig.backend.base import anyconfig.utils -from . import pyyaml +from . import common try: @@ -77,6 +78,8 @@ def yml_fnc(fname, *args, **options): :param args: [stream] for load or [cnf, stream] for dump :param options: keyword args may contain "ac_safe" to load/dump safely """ + options = common.filter_from_options("ac_dict", options) + if "ac_safe" in options: options["typ"] = "safe" # Override it. @@ -93,16 +96,27 @@ def yml_fnc(fname, *args, **options): def yml_load(stream, container, **options): """.. seealso:: :func:`anyconfig.backend.yaml.pyyaml.yml_load` """ - return pyyaml.yml_load(stream, container, yml_fnc=yml_fnc, **options) + ret = yml_fnc("load", stream, **options) + if ret is None: + return container() + + return ret def yml_dump(data, stream, **options): """.. seealso:: :func:`anyconfig.backend.yaml.pyyaml.yml_dump` """ - return pyyaml.yml_dump(data, stream, yml_fnc=yml_fnc, **options) + # .. todo:: Needed? + # if anyconfig.utils.is_dict_like(data): + # if options.get("ac_ordered"): + # factory = collections.OrderedDict + # else: + # factory = dict + # data = anyconfig.dicts.convert_to(data, ac_dict=factory) + return yml_fnc("dump", data, stream, **options) -class Parser(pyyaml.Parser): +class Parser(common.Parser): """Parser for YAML files. """ _cid = "ruamel.yaml"