Skip to content

Commit

Permalink
add "single" parameter to allow a single instance of a dtype, rather …
Browse files Browse the repository at this point in the history
…than a list of such items
  • Loading branch information
chris-simpson authored and timj committed Nov 5, 2024
1 parent 91171ea commit 803eb36
Showing 1 changed file with 31 additions and 5 deletions.
36 changes: 31 additions & 5 deletions python/lsst/pex/config/listField.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ class ListField(Field[List[FieldTypeVar]], Generic[FieldTypeVar]):
deprecated : None or `str`, optional
A description of why this Field is deprecated, including removal date.
If not None, the string is appended to the docstring for this Field.
single : `bool`, optional
If ``single`` is `True`, a single object of ``dtype`` rather than a
list is okay.
See Also
--------
Expand All @@ -320,6 +323,7 @@ def __init__(
minLength=None,
maxLength=None,
deprecated=None,
single=False,
):
if dtype is None:
raise ValueError(
Expand Down Expand Up @@ -352,9 +356,13 @@ def __init__(
raise ValueError("'itemCheck' must be callable")

source = getStackFrame()
if single:
dtype_setup = (List,) + dtype if isinstance(dtype, tuple) else (List, dtype)
else:
dtype_setup = List
self._setup(
doc=doc,
dtype=List,
dtype=dtype_setup,
default=default,
check=None,
optional=optional,
Expand Down Expand Up @@ -390,6 +398,9 @@ def __init__(
to disable checking the list's maximum length).
"""

self.single = single
"""Control whether a single object of dtype is okay."""

def validate(self, instance):
"""Validate the field.
Expand All @@ -415,7 +426,7 @@ def validate(self, instance):
"""
Field.validate(self, instance)
value = self.__get__(instance)
if value is not None:
if not self.single and value is not None:
lenValue = len(value)
if self.length is not None and not lenValue == self.length:
msg = "Required list length=%d, got length=%d" % (self.length, lenValue)
Expand Down Expand Up @@ -444,7 +455,14 @@ def __set__(
at = getCallStack()

if value is not None:
value = List(instance, self, value, at, label)
if not self.single or isinstance(value, list | tuple):
value = List(instance, self, value, at, label)
else:
value = _autocast(value, self.dtype)
try:
self._validateValue(value)
except BaseException as e:
raise FieldValidationError(self, instance, str(e))
else:
history = instance._history.setdefault(self.name, [])
history.append((value, at, label))
Expand All @@ -467,7 +485,10 @@ def toDict(self, instance):
Plain `list` of items, or `None` if the field is not set.
"""
value = self.__get__(instance)
return list(value) if value is not None else None
if isinstance(value, List):
return list(value)
else:
return value

def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
"""Compare two config instances for equality with respect to this
Expand Down Expand Up @@ -511,9 +532,14 @@ def _compare(self, instance1, instance2, shortcut, rtol, atol, output):
return False
if l1 is None and l2 is None:
return True
equal = True
if not isinstance(l1, List):
if not isinstance(l2, List):
return compareScalars(name, l1, l2, dtype=self.dtype[1], rtol=rtol, atol=atol, output=output)
else:
return False
if not compareScalars(f"size for {name}", len(l1), len(l2), output=output):
return False
equal = True
for n, v1, v2 in zip(range(len(l1)), l1, l2):
result = compareScalars(
"%s[%d]" % (name, n), v1, v2, dtype=self.dtype, rtol=rtol, atol=atol, output=output
Expand Down

0 comments on commit 803eb36

Please sign in to comment.