Skip to content

Commit

Permalink
Add tests for JSON and YAML formatter usage
Browse files Browse the repository at this point in the history
Test that we can round trip Pydantic models, dataclasses,
classes that get created from dicts, and tuples using JSON
and YAML formatters.
  • Loading branch information
timj committed Dec 8, 2022
1 parent 554f39a commit ece5f9a
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 0 deletions.
16 changes: 16 additions & 0 deletions python/lsst/daf/butler/tests/_examplePythonTypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@
"MetricsExample",
"registerMetricsExample",
"MetricsExampleModel",
"MetricsExampleDataclass",
)


import copy
import dataclasses
import types
from collections.abc import Mapping
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -274,6 +276,20 @@ def from_metrics(cls, metrics: MetricsExample) -> "MetricsExampleModel":
return cls.parse_obj(metrics.exportAsDict())


@dataclasses.dataclass
class MetricsExampleDataclass:
"""A variant of `MetricsExample` based on a dataclass."""

summary: dict[str, Any] | None
output: dict[str, Any] | None
data: list[Any] | None

@classmethod
def from_metrics(cls, metrics: MetricsExample) -> "MetricsExampleModel":
"""Create a model based on an example."""
return cls(**metrics.exportAsDict())


class ListDelegate(StorageClassDelegate):
"""Parameter handler for list parameters"""

Expand Down
8 changes: 8 additions & 0 deletions tests/config/basic/formatters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,11 @@ StructuredCompositeReadCompNoDisassembly: lsst.daf.butler.tests.testFormatters.M
StructuredDataDataTest: lsst.daf.butler.tests.testFormatters.MetricsExampleDataFormatter
StructuredDataNoComponentsModel: lsst.daf.butler.formatters.json.JsonFormatter
DictConvertibleModel: lsst.daf.butler.formatters.json.JsonFormatter
MetricsExampleA: lsst.daf.butler.formatters.json.JsonFormatter
MetricsExampleB: lsst.daf.butler.formatters.yaml.YamlFormatter
MetricsExampleDataclassA: lsst.daf.butler.formatters.json.JsonFormatter
MetricsExampleDataclassB: lsst.daf.butler.formatters.yaml.YamlFormatter
MetricsExampleModelA: lsst.daf.butler.formatters.json.JsonFormatter
MetricsExampleModelB: lsst.daf.butler.formatters.yaml.YamlFormatter
TupleExampleA: lsst.daf.butler.formatters.json.JsonFormatter
TupleExampleB: lsst.daf.butler.formatters.yaml.YamlFormatter
18 changes: 18 additions & 0 deletions tests/config/basic/storageClasses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,21 @@ storageClasses:
pytype: dict
converters:
lsst.daf.butler.tests.dict_convertible_model.DictConvertibleModel: lsst.daf.butler.tests.dict_convertible_model.DictConvertibleModel.to_dict
# Simple storage classes that can be used for testing with JSON and
# YAML serialization. Need two storage classes for each python type.
MetricsExampleA:
pytype: lsst.daf.butler.tests.MetricsExample
MetricsExampleB:
pytype: lsst.daf.butler.tests.MetricsExample
MetricsExampleDataclassA:
pytype: lsst.daf.butler.tests.MetricsExampleDataclass
MetricsExampleDataclassB:
pytype: lsst.daf.butler.tests.MetricsExampleDataclass
MetricsExampleModelA:
pytype: lsst.daf.butler.tests.MetricsExampleModel
MetricsExampleModelB:
pytype: lsst.daf.butler.tests.MetricsExampleModel
TupleExampleA:
pytype: tuple
TupleExampleB:
pytype: tuple
45 changes: 45 additions & 0 deletions tests/test_datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
DatastoreTestHelper,
DummyRegistry,
MetricsExample,
MetricsExampleDataclass,
MetricsExampleModel,
)
from lsst.daf.butler.tests.dict_convertible_model import DictConvertibleModel
from lsst.resources import ResourcePath
Expand Down Expand Up @@ -918,10 +920,53 @@ def test_pydantic_dict_storage_class_conversions(self):
content = {"a": "one", "b": "two"}
model = DictConvertibleModel.from_dict(content, extra="original content")
datastore.put(model, store_as_model)
retrieved_model = datastore.get(store_as_model)
self.assertEqual(retrieved_model, model)
loaded = datastore.get(store_as_model.overrideStorageClass("NativeDictForConvertibleModel"))
self.assertEqual(type(loaded), dict)
self.assertEqual(loaded, content)

def test_simple_class_put_get(self):
"""Test that we can put and get a simple class with dict()
constructor."""
datastore = self.makeDatastore()
data = MetricsExample(summary={"a": 1}, data=[1, 2, 3], output={"b": 2})
self._assert_different_puts(datastore, "MetricsExample", data)

def test_dataclass_put_get(self):
"""Test that we can put and get a simple dataclass."""
datastore = self.makeDatastore()
data = MetricsExampleDataclass(summary={"a": 1}, data=[1, 2, 3], output={"b": 2})
self._assert_different_puts(datastore, "MetricsExampleDataclass", data)

def test_pydantic_put_get(self):
"""Test that we can put and get a simple Pydantic model."""
datastore = self.makeDatastore()
data = MetricsExampleModel(summary={"a": 1}, data=[1, 2, 3], output={"b": 2})
self._assert_different_puts(datastore, "MetricsExampleModel", data)

def test_tuple_put_get(self):
"""Test that we can put and get a tuple."""
datastore = self.makeDatastore()
data = tuple(["a", "b", 1])
self._assert_different_puts(datastore, "TupleExample", data)

def _assert_different_puts(self, datastore: Datastore, storageClass_root: str, data) -> None:
refs = {
x: self.makeDatasetRef(
f"stora_as_{x}",
dimensions=self.universe.empty,
storageClass=f"{storageClass_root}{x}",
dataId=DataCoordinate.makeEmpty(self.universe),
)
for x in ["A", "B"]
}

for ref in refs.values():
datastore.put(data, ref)

self.assertEqual(datastore.get(refs["A"]), datastore.get(refs["B"]))


class PosixDatastoreTestCase(DatastoreTests, unittest.TestCase):
"""PosixDatastore specialization"""
Expand Down

0 comments on commit ece5f9a

Please sign in to comment.