Skip to content

Commit

Permalink
Merge pull request #1825 from braingram/load_yaml_recursion
Browse files Browse the repository at this point in the history
add `_IgnoreCustomTagsLoader` and use it in `load_yaml`
  • Loading branch information
braingram authored Aug 20, 2024
2 parents 1a8b04b + 196156f commit c5cb4da
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
14 changes: 14 additions & 0 deletions asdf/_tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,17 @@ def test_load_yaml(tmp_path, input_type, tagged):
assert isinstance(tree["a"], asdf.tagged.TaggedDict)
else:
assert not isinstance(tree["a"], asdf.tagged.TaggedDict)


@pytest.mark.parametrize("tagged", [True, False])
def test_load_yaml_recursion(tmp_path, tagged):
fn = tmp_path / "test.asdf"
tree = {}
tree["d"] = {}
tree["d"]["d"] = tree["d"]
tree["l"] = []
tree["l"].append(tree["l"])
asdf.AsdfFile(tree).write_to(fn)
tree = util.load_yaml(fn, tagged=tagged)
assert tree["d"]["d"] is tree["d"]
assert tree["l"][0] is tree["l"]
4 changes: 2 additions & 2 deletions asdf/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ def load_yaml(init, tagged=False):
"""

from .generic_io import get_file
from .yamlutil import AsdfLoader
from .yamlutil import AsdfLoader, _IgnoreCustomTagsLoader

if tagged:
loader = AsdfLoader
else:
loader = yaml.CBaseLoader if getattr(yaml, "__with_libyaml__", None) else yaml.BaseLoader
loader = _IgnoreCustomTagsLoader

with get_file(init, "r") as gf:
reader = gf.reader_until(
Expand Down
23 changes: 23 additions & 0 deletions asdf/yamlutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,29 @@ def represent_numpy_str(dumper, data):
AsdfDumper.add_representer(np.bytes_, AsdfDumper.represent_binary)


class _IgnoreCustomTagsLoader(_yaml_base_loader):
"""
A specialized YAML loader that ignores tags unknown to the
base (safe) loader. This is used by `asdf.util.load_yaml`
to read the ASDF tree as "basic" objects, ignoring the
custom tags.
"""

def construct_undefined(self, node):
if isinstance(node, yaml.MappingNode):
return self.construct_mapping(node)
elif isinstance(node, yaml.SequenceNode):
return self.construct_sequence(node)
elif isinstance(node, yaml.ScalarNode):
return self.construct_scalar(node)
return super().construct_undefined(node)


# pyyaml will invoke the constructor associated with None when a node's
# tag is not explicitly handled by another constructor.
_IgnoreCustomTagsLoader.add_constructor(None, _IgnoreCustomTagsLoader.construct_undefined)


class AsdfLoader(_yaml_base_loader):
"""
A specialized YAML loader that can construct "tagged basic Python
Expand Down
1 change: 1 addition & 0 deletions changes/1825.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Allow ``asdf.util.load_yaml`` to handle recursive objects

0 comments on commit c5cb4da

Please sign in to comment.