asdf-format/asdf

Unable to load reference cycles

braingram opened this issue · 1 comments

Attempting to follow the docs here: https://asdf.readthedocs.io/en/latest/asdf/extending/converters.html#reference-cycles

If I run the following

import fractions

import asdf


# in the example_project.fractions module
class FractionWithInverse(fractions.Fraction):
    def __init__(self, *args, **kwargs):
        self._inverse = None

    @property
    def inverse(self):
        return self._inverse

    @inverse.setter
    def inverse(self, value):
        self._inverse = value


class FractionWithInverseConverter:
    tags = ["asdf://example.com/fractions/tags/fraction-1.0.0"]
    types = [FractionWithInverse]

    def to_yaml_tree(self, obj, tag, ctx):
        return {
            "numerator": obj.numerator,
            "denominator": obj.denominator,
            "inverse": obj.inverse,
        }

    def from_yaml_tree(self, node, tag, ctx):
        obj = FractionWithInverse(node["numerator"], node["denominator"])
        yield
        obj.inverse = node["inverse"]


class FractionWithInverseExtension:
    tags = FractionWithInverseConverter.tags
    converters = [FractionWithInverseConverter()]
    extension_uri = "asdf://example.com/fractions/extensions/fraction-1.0.0"

with asdf.config.config_context() as cfg:
    cfg.add_extension(FractionWithInverseExtension())

    f1 = FractionWithInverse(3, 5)
    f2 = FractionWithInverse(5, 3)
    f1.inverse = f2
    f2.inverse = f1

    with asdf.AsdfFile({"fraction": f1}) as af:
        af.write_to("with_inverse.asdf")

    with asdf.open("with_inverse.asdf") as af:
        reconstituted_f1 = af["fraction"]

    assert reconstituted_f1.inverse.inverse is reconstituted_f1

I get the following error:

AttributeError: 'NoneType' object has no attribute 'inverse'

The produced file contains

fraction: &id001 !<asdf://example.com/fractions/tags/fraction-1.0.0>
  denominator: 5
  inverse: !<asdf://example.com/fractions/tags/fraction-1.0.0>
    denominator: 3
    inverse: *id001
    numerator: 5
  numerator: 3

Tested on asdf main, 2.15, and all minor versions back to 2.8.

I was missing the obj in the yield. With this change the example works on main and 2.15