Skip to content

Commit

Permalink
Relocate pexConfig formatter to here from daf_butler and test it
Browse files Browse the repository at this point in the history
  • Loading branch information
timj committed Aug 21, 2020
1 parent 28d455e commit 6154a31
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 1 deletion.
98 changes: 98 additions & 0 deletions python/lsst/obs/base/formatters/pexConfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This file is part of daf_butler.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from __future__ import annotations

__all__ = ("PexConfigFormatter", )

import os.path

from typing import (
Any,
Optional,
Type,
)

from lsst.utils import doImport
from lsst.daf.butler.formatters.file import FileFormatter


class PexConfigFormatter(FileFormatter):
"""Interface for reading and writing pex.config.Config objects from disk.
"""
extension = ".py"

def _readFile(self, path: str, pytype: Optional[Type[Any]] = None) -> Any:
"""Read a pex.config.Config instance from the given file.
Parameters
----------
path : `str`
Path to use to open the file.
pytype : `type`
Class to use to read the config file.
Returns
-------
data : `lsst.pex.config.Config`
Instance of class ``pytype`` read from config file. `None`
if the file could not be opened.
"""
if pytype is None:
raise RuntimeError("A python type is always required for reading pex_config Config files")

if not os.path.exists(path):
return None
instance = pytype()
# Configs can only be loaded if you use the correct derived type,
# but we can't really store the correct derive type in the StorageClass
# because that'd be a huge proliferation of StorageClasses.
# Instead, we use a bit of a hack: try to load using the base-class
# Config, inspect the exception message to obtain the class we should
# have used, import that, and try it instead.
# TODO: clean this up, somehow.
try:
instance.load(path)
return instance
except AssertionError as err:
msg = str(err)
if not msg.startswith("config is of type"):
raise RuntimeError("Unexpected assertion; cannot infer Config class type.") from err
actualPyTypeStr = msg.split()[-1]
actualPyType = doImport(actualPyTypeStr)
instance = actualPyType()
instance.load(path)
return instance

def _writeFile(self, inMemoryDataset: Any) -> None:
"""Write the in memory dataset to file on disk.
Parameters
----------
inMemoryDataset : `object`
Object to serialize.
Raises
------
Exception
The file could not be written.
"""
inMemoryDataset.save(self.fileDescriptor.location.path)
19 changes: 18 additions & 1 deletion tests/test_butlerFits.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import lsst.utils.tests

import lsst.pex.config
import lsst.afw.image
from lsst.afw.image import LOCAL
from lsst.geom import Box2I, Point2I
Expand Down Expand Up @@ -80,6 +81,12 @@
READ_COMPONENTS = {"bbox", "xy0", "dimensions"}


class SimpleConfig(lsst.pex.config.Config):
"""Config to use in tests for butler put/get"""
i = lsst.pex.config.Field("integer test", int)
c = lsst.pex.config.Field("string", str)


class ButlerFitsTests(DatasetTestHelper, lsst.utils.tests.TestCase):

@classmethod
Expand Down Expand Up @@ -117,7 +124,8 @@ def setUpClass(cls):
# And some dataset types that have no dimensions for easy testing
for datasetTypeName, storageClassName in (("ps", "PropertySet"),
("pl", "PropertyList"),
("pkg", "Packages")
("pkg", "Packages"),
("config", "Config"),
):
storageClass = cls.storageClassFactory.getStorageClass(storageClassName)
addDatasetType(cls.creatorButler, datasetTypeName, {}, storageClass)
Expand Down Expand Up @@ -191,6 +199,15 @@ def testFundamentalTypes(self) -> None:
pkg = Packages.fromSystem()
self.runFundamentalTypeTest("pkg", pkg)

def testPexConfig(self) -> None:
"""Test that we can put and get pex_config Configs"""
c = SimpleConfig(i=10, c="hello")
self.assertEqual(c.i, 10)
ref = self.butler.put(c, "config")
butler_c = self.butler.get(ref)
self.assertEqual(c, butler_c)
self.assertIsInstance(butler_c, SimpleConfig)

def testFitsCatalog(self) -> None:
"""Test reading of a FITS catalog"""
catalog = self.makeExampleCatalog()
Expand Down

0 comments on commit 6154a31

Please sign in to comment.