summaryrefslogtreecommitdiffstats
path: root/_test/test_anchor.py
diff options
context:
space:
mode:
Diffstat (limited to '_test/test_anchor.py')
-rw-r--r--_test/test_anchor.py608
1 files changed, 608 insertions, 0 deletions
diff --git a/_test/test_anchor.py b/_test/test_anchor.py
new file mode 100644
index 0000000..5003428
--- /dev/null
+++ b/_test/test_anchor.py
@@ -0,0 +1,608 @@
+# coding: utf-8
+
+"""
+testing of anchors and the aliases referring to them
+"""
+
+import platform
+from textwrap import dedent
+
+import pytest
+
+from .roundtrip import ( # NOQA
+ YAML,
+ dedent,
+ round_trip,
+ round_trip_dump,
+ round_trip_load,
+)
+
+
+def load(s):
+ return round_trip_load(dedent(s))
+
+
+def compare(d, s):
+ assert round_trip_dump(d) == dedent(s)
+
+
+class TestAnchorsAliases:
+ def test_anchor_id_renumber(self):
+ from ruyaml.serializer import Serializer
+
+ assert Serializer.ANCHOR_TEMPLATE == 'id%03d'
+ data = load(
+ """
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ """
+ )
+ compare(
+ data,
+ """
+ a: &id001
+ b: 1
+ c: 2
+ d: *id001
+ """,
+ )
+
+ def test_template_matcher(self):
+ """test if id matches the anchor template"""
+ from ruyaml.serializer import templated_id
+
+ assert templated_id('id001')
+ assert templated_id('id999')
+ assert templated_id('id1000')
+ assert templated_id('id0001')
+ assert templated_id('id0000')
+ assert not templated_id('id02')
+ assert not templated_id('id000')
+ assert not templated_id('x000')
+
+ # def test_re_matcher(self):
+ # import re
+ # assert re.compile('id(?!000)\\d{3,}').match('id001')
+ # assert not re.compile('id(?!000\\d*)\\d{3,}').match('id000')
+ # assert re.compile('id(?!000$)\\d{3,}').match('id0001')
+
+ def test_anchor_assigned(self):
+ from ruyaml.comments import CommentedMap
+
+ data = load(
+ """
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """
+ )
+ d = data['d']
+ assert isinstance(d, CommentedMap)
+ assert d.yaml_anchor() is None # got dropped as it matches pattern
+ e = data['e']
+ assert isinstance(e, CommentedMap)
+ assert e.yaml_anchor().value == 'etemplate'
+ assert e.yaml_anchor().always_dump is False
+
+ def test_anchor_id_retained(self):
+ data = load(
+ """
+ a: &id002
+ b: 1
+ c: 2
+ d: *id002
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """
+ )
+ compare(
+ data,
+ """
+ a: &id001
+ b: 1
+ c: 2
+ d: *id001
+ e: &etemplate
+ b: 1
+ c: 2
+ f: *etemplate
+ """,
+ )
+
+ @pytest.mark.skipif(
+ platform.python_implementation() == 'Jython',
+ reason='Jython throws RepresenterError',
+ )
+ def test_alias_before_anchor(self):
+ from ruyaml.composer import ComposerError
+
+ with pytest.raises(ComposerError):
+ data = load(
+ """
+ d: *id002
+ a: &id002
+ b: 1
+ c: 2
+ """
+ )
+ data = data
+
+ def test_anchor_on_sequence(self):
+ # as reported by Bjorn Stabell
+ # https://bitbucket.org/ruyaml/issue/7/anchor-names-not-preserved
+ from ruyaml.comments import CommentedSeq
+
+ data = load(
+ """
+ nut1: &alice
+ - 1
+ - 2
+ nut2: &blake
+ - some data
+ - *alice
+ nut3:
+ - *blake
+ - *alice
+ """
+ )
+ r = data['nut1']
+ assert isinstance(r, CommentedSeq)
+ assert r.yaml_anchor() is not None
+ assert r.yaml_anchor().value == 'alice'
+
+ merge_yaml = dedent(
+ """
+ - &CENTER {x: 1, y: 2}
+ - &LEFT {x: 0, y: 2}
+ - &BIG {r: 10}
+ - &SMALL {r: 1}
+ # All the following maps are equal:
+ # Explicit keys
+ - x: 1
+ y: 2
+ r: 10
+ label: center/small
+ # Merge one map
+ - <<: *CENTER
+ r: 10
+ label: center/medium
+ # Merge multiple maps
+ - <<: [*CENTER, *BIG]
+ label: center/big
+ # Override
+ - <<: [*BIG, *LEFT, *SMALL]
+ x: 1
+ label: center/huge
+ """
+ )
+
+ def test_merge_00(self):
+ data = load(self.merge_yaml)
+ d = data[4]
+ ok = True
+ for k in d:
+ for o in [5, 6, 7]:
+ x = d.get(k)
+ y = data[o].get(k)
+ if not isinstance(x, int):
+ x = x.split('/')[0]
+ y = y.split('/')[0]
+ if x != y:
+ ok = False
+ print('key', k, d.get(k), data[o].get(k))
+ assert ok
+
+ def test_merge_accessible(self):
+ from ruyaml.comments import CommentedMap, merge_attrib
+
+ data = load(
+ """
+ k: &level_2 { a: 1, b2 }
+ l: &level_1 { a: 10, c: 3 }
+ m:
+ <<: *level_1
+ c: 30
+ d: 40
+ """
+ )
+ d = data['m']
+ assert isinstance(d, CommentedMap)
+ assert hasattr(d, merge_attrib)
+
+ def test_merge_01(self):
+ data = load(self.merge_yaml)
+ compare(data, self.merge_yaml)
+
+ def test_merge_nested(self):
+ yaml = """
+ a:
+ <<: &content
+ 1: plugh
+ 2: plover
+ 0: xyzzy
+ b:
+ <<: *content
+ """
+ data = round_trip(yaml) # NOQA
+
+ def test_merge_nested_with_sequence(self):
+ yaml = """
+ a:
+ <<: &content
+ <<: &y2
+ 1: plugh
+ 2: plover
+ 0: xyzzy
+ b:
+ <<: [*content, *y2]
+ """
+ data = round_trip(yaml) # NOQA
+
+ def test_add_anchor(self):
+ from ruyaml.comments import CommentedMap
+
+ data = CommentedMap()
+ data_a = CommentedMap()
+ data['a'] = data_a
+ data_a['c'] = 3
+ data['b'] = 2
+ data.yaml_set_anchor('klm', always_dump=True)
+ data['a'].yaml_set_anchor('xyz', always_dump=True)
+ compare(
+ data,
+ """
+ &klm
+ a: &xyz
+ c: 3
+ b: 2
+ """,
+ )
+
+ # this is an error in PyYAML
+ def test_reused_anchor(self):
+ from ruyaml.error import ReusedAnchorWarning
+
+ yaml = """
+ - &a
+ x: 1
+ - <<: *a
+ - &a
+ x: 2
+ - <<: *a
+ """
+ with pytest.warns(ReusedAnchorWarning):
+ data = round_trip(yaml) # NOQA
+
+ def test_issue_130(self):
+ # issue 130 reported by Devid Fee
+ import ruyaml
+
+ ys = dedent(
+ """\
+ components:
+ server: &server_component
+ type: spark.server:ServerComponent
+ host: 0.0.0.0
+ port: 8000
+ shell: &shell_component
+ type: spark.shell:ShellComponent
+
+ services:
+ server: &server_service
+ <<: *server_component
+ shell: &shell_service
+ <<: *shell_component
+ components:
+ server: {<<: *server_service}
+ """
+ )
+ yaml = ruyaml.YAML(typ='safe', pure=True)
+ data = yaml.load(ys)
+ assert data['services']['shell']['components']['server']['port'] == 8000
+
+ def test_issue_130a(self):
+ # issue 130 reported by Devid Fee
+ import ruyaml
+
+ ys = dedent(
+ """\
+ components:
+ server: &server_component
+ type: spark.server:ServerComponent
+ host: 0.0.0.0
+ port: 8000
+ shell: &shell_component
+ type: spark.shell:ShellComponent
+
+ services:
+ server: &server_service
+ <<: *server_component
+ port: 4000
+ shell: &shell_service
+ <<: *shell_component
+ components:
+ server: {<<: *server_service}
+ """
+ )
+ yaml = ruyaml.YAML(typ='safe', pure=True)
+ data = yaml.load(ys)
+ assert data['services']['shell']['components']['server']['port'] == 4000
+
+
+class TestMergeKeysValues:
+
+ yaml_str = dedent(
+ """\
+ - &mx
+ a: x1
+ b: x2
+ c: x3
+ - &my
+ a: y1
+ b: y2 # masked by the one in &mx
+ d: y4
+ -
+ a: 1
+ <<: [*mx, *my]
+ m: 6
+ """
+ )
+
+ # in the following d always has "expanded" the merges
+
+ def test_merge_for(self):
+ from ruyaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2]:
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_keys(self):
+ from ruyaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].keys():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_values(self):
+ from ruyaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].values():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_merge_items(self):
+ from ruyaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ count = 0
+ for x in data[2].items():
+ count += 1
+ print(count, x)
+ assert count == len(d[2])
+
+ def test_len_items_delete(self):
+ from ruyaml import YAML
+
+ d = YAML(typ='safe', pure=True).load(self.yaml_str)
+ data = round_trip_load(self.yaml_str)
+ x = data[2].items()
+ print('d2 items', d[2].items(), len(d[2].items()), x, len(x))
+ ref = len(d[2].items())
+ print('ref', ref)
+ assert len(x) == ref
+ del data[2]['m']
+ ref -= 1
+ assert len(x) == ref
+ del data[2]['d']
+ ref -= 1
+ assert len(x) == ref
+ del data[2]['a']
+ ref -= 1
+ assert len(x) == ref
+
+ def test_issue_196_cast_of_dict(self, capsys):
+ from ruyaml import YAML
+
+ yaml = YAML()
+ mapping = yaml.load(
+ """\
+ anchored: &anchor
+ a : 1
+
+ mapping:
+ <<: *anchor
+ b: 2
+ """
+ )['mapping']
+
+ for k in mapping:
+ print('k', k)
+ for k in mapping.copy():
+ print('kc', k)
+
+ print('v', list(mapping.keys()))
+ print('v', list(mapping.values()))
+ print('v', list(mapping.items()))
+ print(len(mapping))
+ print('-----')
+
+ # print({**mapping})
+ # print(type({**mapping}))
+ # assert 'a' in {**mapping}
+ assert 'a' in mapping
+ x = {}
+ for k in mapping:
+ x[k] = mapping[k]
+ assert 'a' in x
+ assert 'a' in mapping.keys()
+ assert mapping['a'] == 1
+ assert mapping.__getitem__('a') == 1
+ assert 'a' in dict(mapping)
+ assert 'a' in dict(mapping.items())
+
+ def test_values_of_merged(self):
+ from ruyaml import YAML
+
+ yaml = YAML()
+ data = yaml.load(dedent(self.yaml_str))
+ assert list(data[2].values()) == [1, 6, 'x2', 'x3', 'y4']
+
+ def test_issue_213_copy_of_merge(self):
+ from ruyaml import YAML
+
+ yaml = YAML()
+ d = yaml.load(
+ """\
+ foo: &foo
+ a: a
+ foo2:
+ <<: *foo
+ b: b
+ """
+ )['foo2']
+ assert d['a'] == 'a'
+ d2 = d.copy()
+ assert d2['a'] == 'a'
+ print('d', d)
+ del d['a']
+ assert 'a' not in d
+ assert 'a' in d2
+
+ def test_dup_merge(self):
+ from ruyaml import YAML
+
+ yaml = YAML()
+ yaml.allow_duplicate_keys = True
+ d = yaml.load(
+ """\
+ foo: &f
+ a: a
+ foo2: &g
+ b: b
+ all:
+ <<: *f
+ <<: *g
+ """
+ )['all']
+ assert d == {'a': 'a', 'b': 'b'}
+
+ def test_dup_merge_fail(self):
+ from ruyaml import YAML
+ from ruyaml.constructor import DuplicateKeyError
+
+ yaml = YAML()
+ yaml.allow_duplicate_keys = False
+ with pytest.raises(DuplicateKeyError):
+ yaml.load(
+ """\
+ foo: &f
+ a: a
+ foo2: &g
+ b: b
+ all:
+ <<: *f
+ <<: *g
+ """
+ )
+
+
+class TestDuplicateKeyThroughAnchor:
+ def test_duplicate_key_00(self):
+ from ruyaml import YAML, version_info
+ from ruyaml.constructor import DuplicateKeyError, DuplicateKeyFutureWarning
+
+ s = dedent(
+ """\
+ &anchor foo:
+ foo: bar
+ *anchor : duplicate key
+ baz: bat
+ *anchor : duplicate key
+ """
+ )
+ if version_info < (0, 15, 1):
+ pass
+ elif version_info < (0, 16, 0):
+ with pytest.warns(DuplicateKeyFutureWarning):
+ YAML(typ='safe', pure=True).load(s)
+ with pytest.warns(DuplicateKeyFutureWarning):
+ YAML(typ='rt').load(s)
+ else:
+ with pytest.raises(DuplicateKeyError):
+ YAML(typ='safe', pure=True).load(s)
+ with pytest.raises(DuplicateKeyError):
+ YAML(typ='rt').load(s)
+
+ def test_duplicate_key_01(self):
+ # so issue https://stackoverflow.com/a/52852106/1307905
+ from ruyaml.constructor import DuplicateKeyError
+
+ s = dedent(
+ """\
+ - &name-name
+ a: 1
+ - &help-name
+ b: 2
+ - <<: *name-name
+ <<: *help-name
+ """
+ )
+ with pytest.raises(DuplicateKeyError):
+ yaml = YAML(typ='safe')
+ yaml.load(s)
+ with pytest.raises(DuplicateKeyError):
+ yaml = YAML()
+ yaml.load(s)
+
+
+class TestFullCharSetAnchors:
+ def test_master_of_orion(self):
+ # https://bitbucket.org/ruyaml/issues/72/not-allowed-in-anchor-names
+ # submitted by Shalon Wood
+ yaml_str = """
+ - collection: &Backend.Civilizations.RacialPerk
+ items:
+ - key: perk_population_growth_modifier
+ - *Backend.Civilizations.RacialPerk
+ """
+ data = load(yaml_str) # NOQA
+
+ def test_roundtrip_00(self):
+ yaml_str = """
+ - &dotted.words.here
+ a: 1
+ b: 2
+ - *dotted.words.here
+ """
+ data = round_trip(yaml_str) # NOQA
+
+ def test_roundtrip_01(self):
+ yaml_str = """
+ - &dotted.words.here[a, b]
+ - *dotted.words.here
+ """
+ data = load(yaml_str) # NOQA
+ compare(data, yaml_str.replace('[', ' [')) # an extra space is inserted