diff options
author | Hervé Beraud <hberaud@redhat.com> | 2018-11-16 11:27:11 +0100 |
---|---|---|
committer | Hervé Beraud <hberaud@redhat.com> | 2019-02-21 17:34:38 +0100 |
commit | 35dae9c288f7cf8310bf573106500fd317309da0 (patch) | |
tree | cc8a1f62876bf2f006f6cdf999298cff8bb40c71 | |
parent | d67f88623b439e28f014c6ff414cd6d81aa5fd43 (diff) | |
download | oslo-serialization-35dae9c288f7cf8310bf573106500fd317309da0.tar.gz |
Introduce a base yaml parser for all openstack components
A lot of openstack components like:
- solum
- tosca-parser
- heat
- murano
- etc...
reimplement theirs own yaml parser for loading and dumping.
These implementations sometimes forgot to use a safe loader
or safe dumper, our implementation use safe by default.
You can deactive safe by passing the argument is_safe to false when
you call oslo_serialization.yamlutils.load or oslo_serialization.yamlutils.dump.
Change-Id: I63e85a2b4fc999e6acac12ae51c2ab8c64bddbc6
Co-Authored-By: Natal Ngétal <hobbestigrou@erakis.eu>
-rw-r--r-- | oslo_serialization/tests/test_yamlutils.py | 85 | ||||
-rw-r--r-- | oslo_serialization/yamlutils.py | 86 | ||||
-rw-r--r-- | requirements.txt | 1 |
3 files changed, 172 insertions, 0 deletions
diff --git a/oslo_serialization/tests/test_yamlutils.py b/oslo_serialization/tests/test_yamlutils.py new file mode 100644 index 0000000..817ae8c --- /dev/null +++ b/oslo_serialization/tests/test_yamlutils.py @@ -0,0 +1,85 @@ +# Licensed 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. + +import os +import tempfile +import textwrap +import uuid + +from oslotest import base + +from oslo_serialization import yamlutils as yaml + + +class BehaviorTestCase(base.BaseTestCase): + + def test_loading(self): + payload = textwrap.dedent(''' + - foo: bar + - list: + - [one, two] + - {check: yaml, in: test} + ''') + expected = [ + {'foo': 'bar'}, + {'list': None}, + ['one', 'two'], + {'check': 'yaml', 'in': 'test'} + ] + loaded = yaml.load(payload) + self.assertEqual(loaded, expected) + + def test_loading_with_unsafe(self): + payload = textwrap.dedent(''' + !!python/object/apply:os.system ['echo "hello"'] + ''') + loaded = yaml.load(payload, is_safe=False) + expected = 0 + self.assertEqual(loaded, expected) + + def test_dumps(self): + payload = [ + {'foo': 'bar'}, + {'list': None}, + ['one', 'two'], + {'check': 'yaml', 'in': 'test'} + ] + dumped = yaml.dumps(payload) + expected = textwrap.dedent('''\ + - {foo: bar} + - {list: null} + - [one, two] + - {check: yaml, in: test} + ''') + self.assertEqual(dumped, expected) + + def test_dump(self): + payload = [ + {'foo': 'bar'}, + {'list': None}, + ['one', 'two'], + {'check': 'yaml', 'in': 'test'} + ] + tmpfile = os.path.join(tempfile.gettempdir(), str(uuid.uuid4())) + with open(tmpfile, 'w+') as fp: + yaml.dump(payload, fp) + with open(tmpfile, 'r') as fp: + file_content = fp.read() + expected = textwrap.dedent('''\ + - foo: bar + - list: null + - - one + - two + - check: yaml + in: test + ''') + self.assertEqual(file_content, expected) diff --git a/oslo_serialization/yamlutils.py b/oslo_serialization/yamlutils.py new file mode 100644 index 0000000..921b74e --- /dev/null +++ b/oslo_serialization/yamlutils.py @@ -0,0 +1,86 @@ +# Licensed 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. + +"""YAML related utilities. + +The main goal of this module is to standardize yaml management inside +openstack. This module reduce technical debt by avoiding re-implementations +of yaml manager in all the openstack projects. +Use this module inside openstack projects to handle yaml securely and properly. +""" + +import yaml + + +def load(stream, is_safe=True): + """Converts a YAML document to a Python object. + + :param stream: the YAML document to convert into a Python object. Accepts + a byte string, a Unicode string, an open binary file object, + or an open text file object. + :param is_safe: Turn off safe loading. True by default and only load + standard YAML. This option can be turned off by + passing ``is_safe=False`` if you need to load not only + standard YAML tags or if you need to construct an + arbitrary python object. + + Stream specifications: + + * An empty stream contains no documents. + * Documents are separated with ``---``. + * Documents may optionally end with ``...``. + * A single document may or may not be marked with ``---``. + + Parses the given stream and returns a Python object constructed + from the first document in the stream. If there are no documents + in the stream, it returns None. + """ + yaml_loader = yaml.Loader + if is_safe: + if hasattr(yaml, 'CSafeLoader'): + yaml_loader = yaml.CSafeLoader + else: + yaml_loader = yaml.SafeLoader + return yaml.load(stream, yaml_loader) # nosec B506 + + +def dumps(obj, is_safe=True): + """Converts a Python object to a YAML document. + + :param obj: python object to convert into YAML representation. + :param is_safe: Turn off safe dumping. + + Serializes the given Python object to a string and returns that string. + """ + yaml_dumper = yaml.Dumper + if is_safe: + if hasattr(yaml, 'CSafeDumper'): + yaml_dumper = yaml.CSafeDumper + else: + yaml_dumper = yaml.SafeDumper + return yaml.dump(obj, Dumper=yaml_dumper) + + +def dump(obj, fp, is_safe=True): + """Converts a Python object as a YAML document to ``fp``. + + :param obj: python object to convert into YAML representation. + :param fp: a ``.write()``-supporting file-like object + :param is_safe: Turn off safe dumping. + """ + yaml_dumper = yaml.Dumper + if is_safe: + if hasattr(yaml, 'CSafeDumper'): + yaml_dumper = yaml.CSafeDumper + else: + yaml_dumper = yaml.SafeDumper + return yaml.dump(obj, fp, default_flow_style=False, Dumper=yaml_dumper) diff --git a/requirements.txt b/requirements.txt index abdd118..8f3e493 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ six>=1.10.0 # MIT msgpack>=0.5.2 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 pytz>=2013.6 # MIT +PyYAML>=3.12 # MIT |