Skip to content

Commit

Permalink
Enhance file template defaulting behavior
Browse files Browse the repository at this point in the history
* Allow explicit disabling of default value
* Extra tests
  • Loading branch information
timj committed Aug 24, 2018
1 parent a6f5c36 commit b71e4b9
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 14 deletions.
42 changes: 29 additions & 13 deletions python/lsst/daf/butler/core/fileTemplates.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@

"""Support for file template string expansion."""

__all__ = ("FileTemplates", "FileTemplate", "FileTemplatesConfig")

import os.path
import string

from .config import Config


class FileTemplatesConfig(Config):
"""Configuration information for `FileTemplates`"""
pass


Expand All @@ -38,20 +41,32 @@ class FileTemplates:
----------
config : `FileTemplatesConfig` or `str`
Load configuration.
default : `str`, optional
If not `None`, a default template to use if no template has
been specified explicitly in the configuration.
"""

def __init__(self, config, default=None):
self.config = FileTemplatesConfig(config)
self.templates = {}
for name, info in self.config.items():
self.templates[name] = FileTemplate(info)
self.default = FileTemplate(default) if default is not None else None
for name, templateStr in self.config.items():
# We can disable defaulting with an empty string in a config
# or by using a boolean
if name == "default":
if not templateStr:
self.default = None
else:
self.default = FileTemplate(templateStr)
else:
self.templates[name] = FileTemplate(templateStr)

def getTemplate(self, datasetType):
def getTemplate(self, datasetTypeName):
"""Retrieve the `FileTemplate` associated with the dataset type.
Parameters
----------
datasetType : `str`
datasetTypeName : `str`
Dataset type name.
Returns
Expand All @@ -67,21 +82,20 @@ def getTemplate(self, datasetType):
# Get a location from the templates
template = None
component = None
if datasetType is not None:
if datasetType in self.templates:
template = self.templates[datasetType]
elif "." in datasetType:
baseType, component = datasetType.split(".", maxsplit=1)
if datasetTypeName is not None:
if datasetTypeName in self.templates:
template = self.templates[datasetTypeName]
elif "." in datasetTypeName:
baseType, component = datasetTypeName.split(".", maxsplit=1)
if baseType in self.templates:
template = self.templates[baseType]

if template is None:
if "default" in self.templates:
template = self.templates["default"]
if template is None and self.default is not None:
template = self.default

# if still not template give up for now.
if template is None:
raise KeyError("Unable to determine file template from supplied type [{}]".format(datasetType))
raise KeyError(f"Unable to determine file template from supplied type [{datasetTypeName}]")

return template

Expand Down Expand Up @@ -115,6 +129,8 @@ class FileTemplate:
"""

def __init__(self, template):
if not isinstance(template, str) or "{" not in template:
raise ValueError(f"Template ({template}) does not contain any format specifiers")
self.template = template

def format(self, ref):
Expand Down
3 changes: 3 additions & 0 deletions tests/config/templates/templates-bad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file has a bad entry in it where no format specifier is given
calexp: NoFormatSpecifier
default: True
3 changes: 3 additions & 0 deletions tests/config/templates/templates-nodefault.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file disables defaulting
default: False
calexp: "{datasetType}.{component:?}/{datasetType}_v{visit}_f{filter:?}_{component:?}"
2 changes: 2 additions & 0 deletions tests/config/templates/templates-nodefault2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file disables defaulting without specifying any value for default
calexp: "{datasetType}.{component:?}/{datasetType}_v{visit}_f{filter:?}_{component:?}"
2 changes: 2 additions & 0 deletions tests/config/templates/templates-withdefault.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# This file defines a default
default: "{datasetType}.{component:?}/{datasetType}_v{visit}_f{filter:?}_{component:?}"
42 changes: 41 additions & 1 deletion tests/test_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@

"""Test file name templating."""

import os.path
import unittest

from lsst.daf.butler import DatasetType, DatasetRef, FileTemplate, StorageClass, Run
from lsst.daf.butler import DatasetType, DatasetRef, FileTemplates, \
FileTemplate, FileTemplatesConfig, StorageClass, Run

TESTDIR = os.path.abspath(os.path.dirname(__file__))


class TestFileTemplates(unittest.TestCase):
Expand Down Expand Up @@ -112,6 +116,42 @@ def testComponent(self):
with self.assertRaises(KeyError):
self.assertTemplate(tmplstr, "", refWcs)

def testSimpleConfig(self):
"""Test reading from config file"""
configRoot = os.path.join(TESTDIR, "config", "templates")
config1 = FileTemplatesConfig(os.path.join(configRoot, "templates-nodefault.yaml"))
templates = FileTemplates(config1)
ref = self.makeDatasetRef("calexp")
tmpl = templates.getTemplate(ref.datasetType.name)
self.assertIsInstance(tmpl, FileTemplate)

# This config file should not allow defaulting
ref2 = self.makeDatasetRef("unknown")
with self.assertRaises(KeyError):
templates.getTemplate(ref2.datasetType.name)

# Format config file with defaulting
config2 = FileTemplatesConfig(os.path.join(configRoot, "templates-withdefault.yaml"))
templates = FileTemplates(config2)
tmpl = templates.getTemplate(ref2.datasetType.name)
self.assertIsInstance(tmpl, FileTemplate)

# Format config file with bad format string
with self.assertRaises(ValueError):
FileTemplates(os.path.join(configRoot, "templates-bad.yaml"))

# Config file with no defaulting mentioned
config3 = os.path.join(configRoot, "templates-nodefault2.yaml")
templates = FileTemplates(config3)
with self.assertRaises(KeyError):
templates.getTemplate(ref2.datasetType.name)

# Try again but specify a default in the constructor
default = "{datasetType}/{filter}"
templates = FileTemplates(config3, default=default)
tmpl = templates.getTemplate(ref2.datasetType.name)
self.assertEqual(tmpl.template, default)


if __name__ == "__main__":
unittest.main()

0 comments on commit b71e4b9

Please sign in to comment.