Skip to content

Commit

Permalink
add test for generator producing converter
Browse files Browse the repository at this point in the history
remove edge case handling for generator producing
converter not caught by inspect. Any generator producing
converter should be marked as not lazy.
  • Loading branch information
braingram committed Jun 25, 2024
1 parent f463ed0 commit b4b659e
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 15 deletions.
1 change: 0 additions & 1 deletion asdf/_core/_converters/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class AsdfObjectConverter(Converter):
"tag:stsci.edu:asdf/core/asdf-1.1.0",
]
types = ["asdf.tags.core.AsdfObject"]
lazy = True

def to_yaml_tree(self, obj, tag, ctx):
return dict(obj)
Expand Down
99 changes: 99 additions & 0 deletions asdf/_tests/test_lazy_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ def test_cache_clear_on_close(tmp_path):
# to resolve after the with exits
ref = weakref.ref(af["a"])

gc.collect()
assert ref() is None


Expand Down Expand Up @@ -304,3 +305,101 @@ def test_cache_non_weakref():
del obj
gc.collect()
assert cache_item.custom_object == complex(1, 1)


@pytest.fixture(params=[True, False, None], ids=["lazy", "not-lazy", "undefined"])
def lazy_test_class(request):
class Foo:
def __init__(self, data):
self.data = data

tag_uri = "asdf://somewhere.org/tags/foo-1.0.0"

class FooConverter:
tags = [tag_uri]
types = [Foo]

def to_yaml_tree(self, obj, tag, ctx):
return obj.data

def from_yaml_tree(self, node, tag, ctx):
return Foo(node)

lazy = request.param
if lazy is not None:
FooConverter.lazy = lazy
# also set lazy on the class to pass it to the test
Foo.lazy = lazy

class FooExtension:
extension_uri = "asdf://somewhere.org/extensions/minimum-1.0.0"
converters = [FooConverter()]
tags = [tag_uri]

with asdf.config_context() as cfg:
cfg.add_extension(FooExtension())
yield Foo


def test_lazy_converter(tmp_path, lazy_test_class):
obj = lazy_test_class({"a": 1})

fn = tmp_path / "test.asdf"

af = asdf.AsdfFile({"obj": obj})
af.write_to(fn)

with asdf.open(fn, lazy_tree=True) as af:
if lazy_test_class.lazy is None or not lazy_test_class.lazy:
target_class = dict
else:
target_class = AsdfDictNode
assert isinstance(af["obj"].data, target_class)


@pytest.fixture()
def lazy_generator_class(request):

class Foo:
def __init__(self, data=None):
self.data = data or {}

tag_uri = "asdf://somewhere.org/tags/foo-1.0.0"

class FooConverter:
tags = [tag_uri]
types = [Foo]
lazy = True

def to_yaml_tree(self, obj, tag, ctx):
return obj.data

def from_yaml_tree(self, node, tag, ctx):
obj = Foo()
yield obj
obj.data = node

class FooExtension:
extension_uri = "asdf://somewhere.org/extensions/minimum-1.0.0"
converters = [FooConverter()]
tags = [tag_uri]

with asdf.config_context() as cfg:
cfg.add_extension(FooExtension())
yield Foo


def test_lazy_generator_converter(tmp_path, lazy_generator_class):
"""
Test that a converter that returns a generator is not lazy
(even if it's marked as lazy).
"""
obj = lazy_generator_class({"a": 1})

fn = tmp_path / "test.asdf"

af = asdf.AsdfFile({"obj": obj})
af.write_to(fn)

with asdf.open(fn, lazy_tree=True) as af:
assert isinstance(af["obj"].data, dict)
14 changes: 0 additions & 14 deletions asdf/lazy_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import inspect
import warnings
import weakref
from types import GeneratorType

from . import tagged, yamlutil
from .exceptions import AsdfConversionWarning, AsdfLazyReferenceError
Expand Down Expand Up @@ -206,19 +205,6 @@ def _convert_and_cache(self, value, key):
data = _to_lazy_node(value.data, self._af_ref)
sctx = af._create_serialization_context(BlockAccess.READ)
obj = converter.from_yaml_tree(data, tag, sctx)

if isinstance(obj, GeneratorType):
# We can't quite do this for every instance (hence the
# isgeneratorfunction check above). However it appears
# to work for most instances (it was only failing for
# the FractionWithInverse test which is covered by the
# above code). The code here should only be hit if the
# Converter.from_yaml_tree calls another function which
# is a generator.
generator = obj
obj = next(generator)
for _ in generator:
pass
sctx.assign_object(obj)
sctx.assign_blocks()
sctx._mark_extension_used(converter.extension)
Expand Down

0 comments on commit b4b659e

Please sign in to comment.